Repository navigation
perf(agc): reuse address-based dispatch builds with a fault record per use - #8971
Closed
EagleBearerYS wants to merge 28 commits into
Closed
EagleBearerYS wants to merge 28 commits into
EagleBearerYS wants to merge 28 commits into
Conversation
…gistered An address-based build takes the cached address space when it is the device's, of the registry generation read before any lease, and no import was retired since; it then stamps the space's imports as used and brings the space's sparse, writable and heap mirrors up to date. Those steps now live in helpers of their own (spaceMiss, stampSpaceImports, refreshSparseMirrors, appendWritableMirrors), so a later use of an existing build can make the same proof and the same refresh as a build that takes the space. No behaviour change.
A draw whose stages reach guest memory through the BDA table built its resources on every draw: the lease of the registered memory, the upload, the page table and its fault buffer, the descriptor set and the image lookups. boykopovar#2657 skipped the resource cache for such draws because the build was never kept: its completion checked the faults, marked the pages the shader stored to, wrote the written ranges back and released the lease, once. A draw build over the cached address space is now kept when everything it depends on can be proved again cheaply: the space it was built over must be the one a build would take now (the device's, of the current registry generation, with no import retired since), its own regions must be imports or read-only image mirrors with unchanged serials, or snapshot copies whose bytes the draw's snapshots repeat in the same order; the images are proved as for any cached build. The read-only buffers and data words the content key leaves out must equal the build's: a hit with other ones rebinds them per draw, which an address-based build cannot. A reused draw takes the space again and refreshes its mirrors as a build does, and the imports are checked once more after the image proofs. Each draw keeps its own completion: its batch holds the space until then, and its completion checks the faults, marks the written pages and the written ranges served in place. Between draws the build pins no guest allocation, so a guest unmap never waits for the resource cache. The fault buffer, which holds the fault and written-page records, belongs to the build, so one draw at a time uses a build: a draw that repeats it before the previous draw's completion ran builds anew. A build whose draw faulted is not reused. Dispatches and draw recipes do not reuse address-based builds. APS5_NO_ADDRESS_DRAW_REUSE=1 builds every one as before.
The reuse of address-based draw builds rests on choices that are not measured on titles: one draw at a time per build (the fault buffer holds one draw's records), the snapshot list repeated in the same order, and no reuse for dispatches or draw recipes. Recorded as technical debt so they can be revisited with title measurements.
…ation A guest buffer descriptor can describe more than its build binds. Past the device's range limit the build cuts the size at the registered end, or binds an empty buffer when nothing is registered there, and it trims a stride overhang that reaches past the allocation. A reuse compared each descriptor's raw size with what the build had bound, so such a buffer always looked moved: an address-based draw built its resources again on every draw, and a cached build of another draw rebound a trimmed buffer at its raw size, past its allocation. The moved-buffer check and the alias checks a reuse repeats now both take the size from the build's own computation. The alias checks need it as well: with only the moved-buffer check changed, the reused draw failed with "shader buffer aliases the render target", which fails the driver. agc_driver_address_draw_reuse draws twice with a vertex buffer descriptor of 0xffffffff records whose render target lies above its 64 KiB allocation. The second draw must reuse the first one's build; without the fix it built again.
Both walks of the reuse proof skip a rejected image-table element, so it is never proved again. Two rejections are transient (a surface that is not mapped, a texture lookup that throws), and pages committed in a registered reserved range leave the registry generation alone: an address-based build could be reused with the element still rejected after its surface became valid. Such a build is now not reusable. The address-based proof could also throw out of Revalidate and fail the draw, where a fresh build would reject the element or report the error itself. Anything the proof throws now refuses the reuse.
A batch whose end or submit fails is discarded without running its completions. The use count BeginUse took was only dropped by CompleteUse, so the build stayed in use and was never reused again. The count now goes with the token BeginUse returns, which the completion owns: it drops when the completion runs or is discarded. The mock's queue submit can now fail for the test.
A fresh capture lists a draw's snapshot regions sorted by address (ShaderMemory::Regions). A decode-cache hit lists the stages' code and their matched regions in stage order instead. SameSnapshots compares the two lists entry by entry, so the first hit after a decode miss failed the address-based reuse and built the draw again whenever the orders differed, as they do when the pixel shader's code lies below the vertex shader's. The draw now sorts its snapshots by address before it hands them on.
…rofile A tester of address-based draw reuse could not tell how often a draw reused its build: the driver counted no reuse, and two runs averaged 4.4 and 10.2 ms per address-based draw where others took about 1 ms. A refused reuse pays for its proof and then for the build, so the refusals and their reasons are what tell such runs apart. The resource cache now counts, for address-based draw builds, the proofs Revalidate runs, the reuses, and the refusals by reason: in use (the build's previous draw has not completed; refused before any proof), not reusable (its draw faulted), snapshots differ, the space did not resume (by its state, a replaced space, the device, the registry generation, the import epoch, or a sparse mirror that could not be refreshed), the image or buffer proof failed, the imports changed, the proof threw, and read-only buffers that moved after the proof passed. The time of refused proofs is summed under APS5_PROFILE_DRAW. A reused draw's hold of its space is counted until its use ends, and host import refusals, over the budget or of an import that would replace a held one, are counted with how many came while such a hold was live. The reused flag travels in the use token BeginUse returns, so the hold is released with the use count when the token goes, also when a batch is discarded without running its completions. A proof that throws is counted on its own, as "threw": the throw can come from resuming the space, from the proof or from the imports check, and counting it under any of those would hide an error among ordinary refusals. APS5_PROFILE_DRAW prints them every 10 s right after the [rescache] draws line, beside the pin-contention waits of the same window (the [address-sync] leases line prints those cumulatively on its own timer): "[rescache] address-based draws: P proofs, R reused; refused: in use N, ..., threw N, moved buffers N; ...; host import refusals: budget B (H while a reused draw held its space), held import ...; pin-contention waits W (T ms)". agc_driver_address_draw_reuse reads the counters: repeated unchanged draws count as reuses, a draw repeated before the previous one completed counts one refusal in use, a draw after the guest rewrote its pixel program counts one refusal for a changed snapshot, a draw after a guest write into memory it reads in place counts a reuse, the draw after a fault counts one refusal of a build that is no longer reusable, and a proof that throws counts one refusal that threw. The BDA resource tests check that a reused draw's hold is counted while its batch is pending and released when the batch fails to submit.
…ss reuse proof
The reuse proof of an address-based draw build compares the draw's
snapshots with guest memory for live regions and with the build's
copies for the others. A write that earlier work in the batch recorded
over a snapshot's range (a fill, a copy, a dispatch storing through a
descriptor) may still be pending when the proof runs: memory then still
holds the bytes the capture saw, the proof passes, and the draw runs
after the write on a program the decoder specialized on the old bytes.
Revalidate now syncs the recorder through every snapshot range that a
recorded write overlaps before it compares the snapshots, so the
comparison sees the memory the earlier work leaves, and a capture made
before that write is refused as a changed snapshot. A range with no
pending write costs one overlap test. Stores through the BDA table are
not recorded as pending writes; a later use reads them in place.
agc_driver_address_draw_reuse gains the case: a draw whose snapshots
include the guest pointer its pixel shader reads, reused once, then a
FillBuffer recorded over that pointer and the same draw with the old
capture in the same batch. The draw is refused for its snapshots,
builds again and reads the new pointer; without the sync it was reused
("1 reuses, 0 builds").
Main's boykopovar#7044 gives the build and the dispatch move proof one size function for a guest buffer, ShaderResources::guestBufferBytes, as this branch's GuestBufferBytes does for the build, the draw move check and the address reuse proof. Keep main's clamping in the static GuestBufferBytes, have guestBufferBytes call it, and drop this branch's two helpers. Main's depth guard counter and debt entry stay beside this branch's.
Keeps this branch's configurable mock submit result next to main's mockCmdCopyBuffer in the graphics tests.
Keeps this branch's guestBufferBytes next to main's longer addGuestBuffer signature in ShaderResources.cpp.
importAllocation takes main's step-down for a held or over-budget import and its pinned byte count; a refusal is counted only where the import is refused, not where it steps down. A cached space is reused only when every heap and sparse mirror is still watched or cannot be imported now (main's check, spaceMirrorsKept), at acquisition and at ResumeSpace, and stamping its imports follows main's once-per-space use stamps. The BDA test access keeps both new hooks, submitResult and limitImports.
…th watched imports jotitadev's macOS run of agc_driver_address_draw_reuse failed at its ThrowingProof case while imports stay watched: the fast proof found the sampled image's memory unchanged after MultisampleTargetView took it over, and the build was reused. Main's 67575aa, merged in the parent, checks every ordinary sampled and storage range against the multisampled color targets before either proof, so the case is refused before the fast proof runs; no Linux test forced watched imports. agc_driver_address_draw_reuse_watched_imports runs the test with APS5_WRITE_WATCH_IMPORTS=watch where the switch exists (not on Windows), and the throwing proof also runs on a draw build without the address table, whose proof throws as its fresh build does. Without that check, the watched run fails at "an address-based draw build whose sampled image's memory became a multisampled target was reused", and both runs fail at the build without the address table.
…erflows A use whose stores through the BDA table touch more than 2,048 chunks of 32 pages, or whose shader finds no free slot for a chunk within its 8 linear probes, sets the record's overflow word. Its completion then threw "more than 2048 chunks ... are not implemented": the pages the record did hold were not marked as written, the write-back was reported as failed, and an address-based build stopped being reusable. The completion now marks the chunks the record holds, then every writable range of the build's page table, and clears the overflow word. That is a superset of what the GPU stored, since a store outside those ranges faults instead; the guest memory caches over them revalidate once. The first overflow of a process is reported on stderr. The handling follows fable-void's 3b1372e on wip/new-gpu-path ("attribute faults in shared records by access ranges and survive their overflow"), which applies the chunks a shared batch record holds and then marks the write ranges of every build sharing it. A record here belongs to one build, whose stores can reach any writable range of its table, so those ranges are the superset. Rebased onto boykopovar#6636's head fc9c541: the only conflict was the debt list, where main's new entries and this one are both kept.
…r use An address-based compute object was never reusable: noteReusable gave up on any build with usesBda that was not a draw, so every dispatch of a program that reaches guest memory through the BDA table rebuilt its resources, its page table and image table included. Dispatch builds now take boykopovar#6636's rules for draws. A build whose stages are address-based is reusable (APS5_NO_ADDRESS_DISPATCH_REUSE=1 builds each dispatch), and a hit runs Revalidate's address branch: the snapshots, the cached address space (refused when it was replaced or its device, registry generation or import epoch moved, or a mirror could not be refreshed), the build's image and buffer proof and the space's imports. Each dispatch takes the space for its batch and suspends it after recording, so between dispatches the build pins no guest allocation and a guest free does not wait for it. Reuses and refusals go to the address reuse counters, whose [rescache] line now reads "address-based draws and dispatches". Unlike a draw, a dispatch is not refused while an earlier dispatch of its build is in flight, since several dispatches of one program per batch are common. The build's fault buffer holds the fault report and written-page record of one use, so the first use in flight takes it and every other concurrent use takes a spare: a fault buffer of its own and a descriptor set that repeats the build's writes with that buffer in the fault binding. The use's completion checks its own record, so a fault is reported by the dispatch that made it and each dispatch marks its own written pages. The spare returns to the build's pool when the use ends and serves a later concurrent use; the build's descriptors do not change after the build, so a pooled set stays valid. A fault makes the build non-reusable, as for draws. Dispatch recipes still require a build without completion work, so an address-based build never gets one: a recipe's validation proves neither the space nor the snapshots. A record per use follows fable-void's 26ff0b4 on wip/new-gpu-path ("take each dispatch's BDA table and fault record from pooled slabs"). It replaces the shared batch record of his a7015cd ("share one fault record among the address-based builds of a batch"), whose faults needed 3b1372e's attribution by access ranges; a record that one use owns needs no attribution. In BF-033's dispatch binding harness on lavapipe (one wave per dispatch, APS5_IMAGE_TABLE_CAPACITY=4096, APS5_LOOKUP_LOG_BOUND=8192 at 2,000 textures), the driver's time per address-based dispatch, as the median of five 10 s windows, on boykopovar#6636's head and with this change: textures one dispatch per submission eight per submission 1 374 -> 77 us 1,684 -> 24 us 16 519 -> 76 us 1,600 -> 28 us 128 798 -> 123 us 1,845 -> 57 us 2,000 5,032 -> 699 us 2,232 -> 647 us The head builds on every dispatch (no cache hit); with the change each run builds once and reuses the build for the rest, eight concurrent uses per batch included. agc_driver_address_dispatch_reuse covers the cases, each red on boykopovar#6636's head (89ab0ea) and green here: - repeated dispatches with unchanged inputs are reused and counted, four in one batch over two blocks too; - a changed word of the program snapshot rebuilds and counts one snapshot refusal; - of two reused dispatches in one batch, only the faulting one reports a fault, and two faulting ones each report their own address; - a dispatch rewriting the SRT that a later reused dispatch of the batch reads is seen by it, and a fill recorded over a captured SRT refuses the reuse as a changed snapshot; - a guest free while a reused build is cached, and while a reused dispatch is in flight, returns at once; - a dispatch storing to 3,072 chunks overflows its written-page record without failing and is reused after it. The BDA resource unit test (agc_driver_graphics) expected an address-based dispatch build not to be reusable; it now expects one to be reusable unless APS5_NO_ADDRESS_DISPATCH_REUSE=1 is set. Rebased onto boykopovar#6636's head fc9c541, which carries main's 89d255c (compute templates served with moved read-only buffers) and the SRT guard word in the fault buffer. Main's moved-buffer proof refuses address-based builds, so an address-based hit skips it, in PrepareDispatch and in the dispatch; every other hit takes main's path unchanged, and a dispatch binds either main's moved-buffer set or its use's fault-record set. A use's own fault record clears the SRT guard word as the build's does, and the guard is read from that record. As WriteBackBuffers does since main's c0588d2, a use that reports an unaligned BDA access marks its writes before the report; the new unaligned case runs such a use.
With Celegans12's change 8104, an address-based compute build stages a written element in device memory as an overlay region of its import: the region is a GPU copy (gpuCopy, deviceLocal) with no direct pointer. SpaceRegions refused every region that was neither direct nor a fixed mirror, so noteReusable never marked such a build reusable and each dispatch of it rebuilt. A staged region is safe to reuse. proveBuild records its copy-in from the import anew for each reuse (RecordStagingCopies), MarkGpuWrites records the copy-back of its written ranges after each use (RecordCopyBacks), and its range is held to the serial of the import it lies in like a direct region. Count it as a space region. Address-based builds do not stage before that change, so on its own this changes nothing.
|
This pull request is labeled |
13 tasks done
|
Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.
Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.
|
main brings boykopovar#8224, which cuts a read-only strided buffer that runs less than one stride into the render target at the target, in addGuestBuffer and in CheckBufferAliases. CheckBufferAliases keeps this branch's GuestBufferBytes size and applies main's cut to it, as addGuestBuffer does after guestBufferBytes.
…uild does Since boykopovar#8224, a read-only strided buffer that runs less than one stride into the render target is bound only up to the target, and the cached build keeps that size. MovedReadOnlyBuffers compared it with the size without that cut, so a hit with the same words took the buffer as moved and rebound all of it, over the render target, and an address-based build holding such a buffer was never reused. GuestBufferBytes takes the target and applies the cut. The build, CheckBufferAliases and MovedReadOnlyBuffers pass the draw's target for read-only buffers, so all three size a buffer the same way. agc_driver_graphics checks a cached build whose read-only stride-60, 4-record buffer runs 8 bytes into the target: the same words list nothing moved, a buffer moved to run 4 bytes in is listed at 236 bytes, and one moved to run a whole stride in keeps its 240.
The entry described each proof the reuse makes. It now names where they are (ShaderResources::Revalidate) and keeps what stays open: one draw at a time per build, no reuse for dispatches and draw recipes, and no measurement in a title. It goes from 1,537 to 871 characters.
12 tasks done
boykopovar#6636 merges main (with boykopovar#8224), sizes a moved read-only buffer at the render target as its build does, and cuts its debt entry to the gaps. The entry here keeps that shorter form and adds this branch's dispatches: the dispatch switch, a dispatch's own fault buffer and descriptor set for a repeat in one batch, and recipes for both.
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A dispatch whose program reaches guest memory through the BDA table (an address-based dispatch) builds its resources again every time: the lease of the registered memory, the upload, the page table and its fault buffer, the image table and the descriptor set.
noteReusablegives up on any address-based build that isn't a draw, and #6636, which this PR stacks on, makes only draws reusable.The change. Dispatch builds take #6636's rules for draws. A build whose stages are address-based is reusable, and a hit runs
Revalidate's address branch:Each dispatch takes the space for its batch and suspends it after recording. Between dispatches the build pins no guest allocation, so a guest free doesn't wait for it.
One difference from draws. A dispatch isn't refused while an earlier dispatch of its build is in flight, because several dispatches of one program per batch are common.
Main's template path.
main's 89d255c serves a compute dispatch from its cached template when only read-only buffers moved. Non-address hits keep that path exactly. Address-based hits skip its proof, which always refuses them. Both then runRevalidate(shaders, nullptr, snapshots), and a dispatch binds either main's moved-buffer set or its use's record set, never both.Overflowing written-page records (first commit).
Prior art. The record per use follows fable-void's 26ff0b4 on
wip/new-gpu-path("take each dispatch's BDA table and fault record from pooled slabs"), in place of his shared batch record (a7015cd). The overflow handling follows his 3b1372e. Both commit bodies name the source commits.Profile counters. Reuses and refusals go to #6636's address reuse counters, and its
[rescache]line now reads "address-based draws and dispatches".APS5_NO_ADDRESS_DISPATCH_REUSE=1builds every dispatch as before, separately from #6636'sAPS5_NO_ADDRESS_DRAW_REUSE=1. Both are indocs/dev/ENVIRONMENT.md.What it leaves out (also in the debt file):
Cost. A dispatch binding harness on lavapipe, one wave per dispatch. The driver's time per address-based dispatch, as the median of five 10-s windows, on #6636's head fc9c541 and with this PR:
#6636's head has no resource-cache hit on any row. This PR has one miss per run, and reuses the build for the rest, eight concurrent uses per batch included.
These runs cap the image table at 4,096 entries on both sides (
APS5_IMAGE_TABLE_CAPACITY=4096, 3640795's cap applied by hand;mainhas no such cap). Without it, each spare record's descriptor set is allocated at the device's full image-table capacity (16 MiB on lavapipe, about 100 ms to create), and a build keeps its spares until its uses are reaped. That is fixed before this leaves draft.Pending: Resident Evil Village (PPSA01556)'s first scene, where dispatches bound the frame. The comparison is the default against
APS5_NO_ADDRESS_DISPATCH_REUSE=1, with fps over 5 minutes at control and the[rescache]address-based line. It will be added here before this leaves draft.Overlap. Checked with
git merge-treeagainst every open PR on the same files, onmain9be6ccf. Beyond #6636's own list, which this PR inherits:SpaceRegions()refuses a region that is neither direct nor a fixed read-only mirror. perf(agc): stage the written elements of address-based compute builds in device memory #8104's overlay region is a GPU copy with no direct pointer, so a build that stages an element was never reusable.gpuCopy && deviceLocal) as a space region. On every reuse,proveBuildrecords its copy-in from the import again, andMarkGpuWritesrecords the copy-back after each use. Its range is held to its import's serial, like a direct region.test/bf099-with-8104on the fork, perf(agc): stage the written elements of address-based compute builds in device memory #8104's commit unchanged) adds anoverlaycase: a one-thread program reads a staged word, adds 1 and stores it. Dispatches see 0x100, then 0x201 after a CPU store of 0x200, then 0x203 after two reused dispatches in one batch. All are counted as reused.noteGpuConstantChecks(), and the counters it resets, read and reset the build's fault buffer, beside this PR'sCheckFault(Buffer& record)andmarkWrittenPages(record). Whichever merges second has them read and reset the use's record.BdaResources.hpp/.cpp. Whichever merges second takes this PR's spare records from that arena too, so a spare doesn't keep a full fault buffer for the life of its build.VulkanDevice::recordDispatch), at the lines where this PR completes a reused dispatch's use. Whichever merges second runs the HTILE completion in the reused-use branch too.The commits:
fix(agc): mark a BDA written-page record's writable ranges when it overflows.perf(agc): reuse address-based dispatch builds with a fault record per use. This includesAddressDispatchReuse.cpp, the debt entries, theENVIRONMENT.mdrow and theagc_driver_graphicsexpectation.fix(agc): keep address-based builds with a staged overlay reusable (for perf(agc): stage the written elements of address-based compute builds in device memory #8104, above).Tested
agc_driver_address_dispatch_reuse(tests/execution/AddressDispatchReuse.cpp, lavapipe). Each case is red with the second commit's non-test hunks reverted, and green here:repeated: four repeated dispatches with unchanged inputs are reused and counted, including four in one batch over two blocks. Red: "4 of 4 repeated address-based dispatches with unchanged inputs built their resources again".snapshot: a changed word of the program snapshot rebuilds and counts one snapshots refusal.faults: of two reused dispatches in one batch, only the one storing into read-only memory reports its fault, once, and the other's store lands. The next dispatch counts one "not reusable" and rebuilds, and two faulting dispatches in one batch each report their own address.srt: a dispatch that stores a new pointer into the SRT through the BDA table is seen by a reused reader later in the batch. AFillBufferrecorded over a captured SRT refuses the reuse as a changed snapshot.free: a guest free while a reused build is cached, and while a reused dispatch is in flight, returns at once, and later dispatches are correct.overflow: 1,024 threads × 3 stores into 3,072 distinct 128 KiB chunks overflow the record without failing. Every store lands, and the build is reused afterwards.unaligned: a reused dispatch whoseglobal_atomic_addis 2 bytes off marks its writes and stops the build's reuse.faultsreports a fault twice. With the pre-proof sync removed,srtreuses the dispatch after the recorded fill.agc_driver_address_dispatch_reuse --repeat until-fail:20: 20 of 20. perf(agc): reuse address-based draw builds whose inputs are unchanged #6636'sagc_driver_address_draw_reuseand_watched_importspass.--repeat until-fail:20.^agcgives 850 of 851, the sameagc_driver_sgpr_pattern_fill_keysfailure under load (5 of 5 alone).^agc(Linux, lavapipe, CI configuration): 851 of 851 with the reuse on, and 850 of 851 withAPS5_NO_ADDRESS_DISPATCH_REUSE=1. perf(agc): reuse address-based draw builds whose inputs are unchanged #6636's head gives 849 of 850. The one failure in both of the last two isagc_driver_sgpr_pattern_fill_keysunder load, which passes 5 of 5 alone on both trees.mainwith fix(agc): bind a strided buffer's last record only up to the render target it runs into #8224 and cuts its debt entry, so this PR's entry keeps that shorter form with its dispatches added), on GitHub's runners (run):agc_driver_address_dispatch_reuse, both draw-reuse registrations,agc_driver_graphicsand the tests tied to the changed files pass 5 runs in a row on Linux (31 tests). On Windows 27 pass and 2 skip without Vulkan. The suites pass on both.precise_waittiming test failed on both sides.agc_driver_graphicsfails with perf(agc): reuse address-based draw builds whose inputs are unchanged #6636's code, and passes ×10 with this PR. The two device tests skip without a Vulkan device. The suites pass.pipelined_indirect_drawandsgpr_pattern_fill_keys, and perf(agc): reuse address-based draw builds whose inputs are unchanged #6636's headpipelined_fill. Each passes 5 of 5 alone on both trees.check_test_registration.pyandcheck_skips.pymatch on both.check_conventions.pypasses, with no comment lines added.Checklist
main27c88a8), merged in; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)