examples: the module surface, and one header kept for the contrast - #588
Merged
Conversation
Sunrisepeak
force-pushed
the
feat/examples-module-surface
branch
3 times, most recently
from
September 7, 2026 19:44
2a968b3 to
bbecc48
Compare
The payload lane and the boundary lane are separate, and each example now shows one of them. `vulkan` reaches its SPIR-V through a MODULE. `src/vulkan/saxpy.cpp` opened with `#include "scale_comp.h"` -- a name no line in the project produced and no reader could derive without opening the rule -- and now imports `vulkan_saxpy.shaders`, whose name and whose `scale_comp()` accessor are both derived from names the project already wrote. The accessor answers with the address and the byte count together, which is what makes `sizeof` the wrong question rather than merely an awkward one: the rule may put the words in an object, and there is then no array to take the size of. `cuda` and `sycl` GENERATE their `extern "C"` boundary. Each entry point is marked with `MCPP_EXPORT_C` where it is defined, and `mcpp.tools.island` writes the header the island's compiler reads and the module the seam imports. `include/saxpy/saxpy.h` is gone from both. Both halves are handed to the scan, so a signature that drifted between the island and the CPU fallback is refused while the boundary is generated -- the one point at which both texts exist at once, and the only check available at a boundary where C language linkage does not mangle and the two halves are never in one link. The island names nothing and the host half writes one `#include`, and that asymmetry is measured rather than incidental. The island is compiled by a driver mcpp did not invoke, so its rule takes forced-include flags for that one command line. The host half is ordinary project C++, and the only project-wide channel would force the header into every C++ translation unit including the seam, where declarations ahead of `export module` are ill-formed. `hip` keeps its hand-written header, and its README says why. It computes the same thing through the same seam as `cuda`, so the difference between the two examples is exactly this one thing, and the hand-written form is what every C boundary looks like today. Neither is deprecated; the generated form is the default because the copy it removes is the one that goes wrong quietly. `multi-backend` reaches its SPIR-V as `opkit.shaders` too, and keeps `include/opkit/opkit.h` written by hand -- which is the distinction this example is for. That header is a LIBRARY boundary: consumers outside this tree compile against it, so it is not an intermediate and generating it would not remove a copy, it would move one. The shader payload is an intermediate, and it moves. `examples/10-graphics/offscreen` moves too, and it is the one example CI both builds AND RUNS on two platforms -- so the surface is exercised there rather than merely compiled. Two includes become one import, and the two stages are `offscreen::shaders::triangle_vert()` and `::triangle_frag()`. `cann` is unchanged. Its boundary has a different shape -- `saxpy_device` between the seam and a host glue file, `saxpy_launch` between that glue and the kernel -- and both halves of the first one are compiled by mcpp, so the forced-include asymmetry above does not apply. Its README already records that the example does not build yet. Verified on this host: cuda and sycl build and run on an RTX 4080 with the generated boundary on the island's own command line, vulkan runs on the same device with its shader reached as a module, multi-backend runs on lavapipe with `--accel "vulkan1.2"`, the offscreen triangle renders on lavapipe at centre pixel `(124, 70, 62, 255)` -- the same pixel its CPU leg produces, which is the assertion CI makes -- and each CPU leg builds and runs under `--no-accel`. `docs/20`'s two-lanes section said the seam is written by hand and the shader lane's equivalent is generated. Half of that still holds: the seam is a design decision. What sits under it is each signature written a second time, and that half is now generated too. The section says so, and gains the table that maps a file name to the call it arrives as -- which is the question a reader of that document actually has. Every example under `examples/09-heterogeneous` and `examples/10-graphics` pins 0.3.0 now, `hip` and `cann` included. Neither uses anything new, and leaving them behind would be defensible in isolation -- but `hip` exists to be read beside `cuda`, and a reader comparing the two would find a second difference the text never explains. The floor sentences move with them. The six plugin pins in `docs/05` and `docs/20` move too, in both languages. A version in a document is the line someone pastes, and 0.2.1 and 0.2.4 predate every declaration those sections now describe -- a reader who copied one would get a build in which the rules never route. The Windows graphics run step was brought back as a MEASUREMENT and is withdrawn again, this time with an answer. Four rounds established what it is not -- a missing `vulkan-1.dll`, an unparseable ICD manifest (a real defect, fixed in openxlings/xim-pkgindex#781, and not this failure's cause), a failed `LoadLibrary`, or the driver-files variable's vintage -- and the last round asked the loader instead of guessing: INFO: Loader is running with elevated permissions. Environment variable VK_DRIVER_FILES will be ignored INFO: Loader is running with elevated permissions. Environment variable VK_ICD_FILENAMES will be ignored ERROR | DRIVER: Registry lookup failed to get ICD manifest files. A GitHub Windows runner runs elevated, and the loader discards every driver-path environment variable when it is -- it will not let a path a non-administrator could write inject a driver into an elevated process. It falls back to the registry, which has no ICD. No package change reaches that; the mechanism this example uses on Linux and macOS is unavailable to an elevated process on Windows, and reaching lavapipe there means a registry entry under `HKEY_LOCAL_MACHINE\SOFTWARE\Khronos\Vulkan\Drivers` or an unelevated run. The four eliminations and the loader's own words are recorded beside the withdrawn step, so the next attempt starts from there rather than from the first hypothesis. Windows keeps building the Vulkan half and running the CPU fallback. Requires mcpp:plugins 0.3.0.
Sunrisepeak
force-pushed
the
feat/examples-module-surface
branch
from
September 7, 2026 20:10
bbecc48 to
3f6f75f
Compare
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.
examples: the module surface, and one header kept for contrast
The payload lane and the boundary lane are separate, and each example now
shows one of them.
vulkanreaches its SPIR-V through a MODULE.src/vulkan/saxpy.cppopenedwith
#include "scale_comp.h"-- a name no line in the project produced andno reader could derive without opening the rule -- and now imports
vulkan_saxpy.shaders, whose name and whosescale_comp()accessor are bothderived from names the project already wrote. The accessor answers with the
address and the byte count together, which is what makes
sizeofthe wrongquestion rather than merely an awkward one: the rule may put the words in an
object, and there is then no array to take the size of.
cudaandsyclGENERATE theirextern "C"boundary. Each entry point ismarked with
MCPP_EXPORT_Cwhere it is defined, andmcpp.tools.islandwrites the header the island's compiler reads and the module the seam
imports.
include/saxpy/saxpy.his gone from both. Both halves are handed tothe scan, so a signature that drifted between the island and the CPU
fallback is refused while the boundary is generated -- the one point at which
both texts exist at once, and the only check available at a boundary where C
language linkage does not mangle and the two halves are never in one link.
The island names nothing and the host half writes one
#include, and thatasymmetry is measured rather than incidental. The island is compiled by a
driver mcpp did not invoke, so its rule takes forced-include flags for that
one command line. The host half is ordinary project C++, and the only
project-wide channel would force the header into every C++ translation unit
including the seam, where declarations ahead of
export moduleareill-formed.
hipkeeps its hand-written header, and its README says why. It computes thesame thing through the same seam as
cuda, so the difference between the twoexamples is exactly this one thing, and the hand-written form is what every C
boundary looks like today. Neither is deprecated; the generated form is the
default because the copy it removes is the one that goes wrong quietly.
multi-backendreaches its SPIR-V asopkit.shaderstoo, and keepsinclude/opkit/opkit.hwritten by hand -- which is the distinction this exampleis for. That header is a LIBRARY boundary: consumers outside this tree compile
against it, so it is not an intermediate and generating it would not remove a
copy, it would move one. The shader payload is an intermediate, and it moves.
examples/10-graphics/offscreenmoves too, and it is the one example CI bothbuilds AND RUNS on two platforms -- so the surface is exercised there rather
than merely compiled. Two includes become one import, and the two stages are
offscreen::shaders::triangle_vert()and::triangle_frag().cannis unchanged. Its boundary has a different shape --saxpy_devicebetween the seam and a host glue file,
saxpy_launchbetween that glue andthe kernel -- and both halves of the first one are compiled by mcpp, so the
forced-include asymmetry above does not apply. Its README already records
that the example does not build yet.
Verified on this host: cuda and sycl build and run on an RTX 4080 with the
generated boundary on the island's own command line, vulkan runs on the same
device with its shader reached as a module, multi-backend runs on lavapipe with
--accel "vulkan1.2", the offscreen triangle renders on lavapipe at centrepixel
(124, 70, 62, 255)-- the same pixel its CPU leg produces, which is theassertion CI makes -- and each CPU leg builds and runs under
--no-accel.docs/20's two-lanes section said the seam is written by hand and the shaderlane's equivalent is generated. Half of that still holds: the seam is a design
decision. What sits under it is each signature written a second time, and that
half is now generated too. The section says so, and gains the table that maps a
file name to the call it arrives as -- which is the question a reader of that
document actually has.
Every example under
examples/09-heterogeneousandexamples/10-graphicspins 0.3.0 now,
hipandcannincluded. Neither uses anything new, andleaving them behind would be defensible in isolation -- but
hipexists tobe read beside
cuda, and a reader comparing the two would find a seconddifference the text never explains. The floor sentences move with them.
The six plugin pins in
docs/05anddocs/20move too, in both languages.A version in a document is the line someone pastes, and 0.2.1 and 0.2.4
predate every declaration those sections now describe -- a reader who copied
one would get a build in which the rules never route.
The Windows graphics run step comes back, as a measurement. It was written
once, failed, and came out with the wrong cause recorded; the cause has since
been found in
xim:mesa-lavapipe, which wrote its rewrittenlibrary_pathinto the ICD's JSON string unescaped --
C:\Users\...carries\U, which isnot a JSON escape, so the loader's cJSON parser rejected the manifest and
skipped the ICD silently. Fixed in openxlings/xim-pkgindex#781.
The step removes the lavapipe store entry before rebuilding, and that is not
tidiness:
install()does not re-run over an existing payload and~/.mcppis cached across runs, so a runner that installed 26.2.0 before the fix would
keep the manifest it was given and the step would measure the cache. It also
prints the manifest and asserts it PARSES, so a failure is read against what
the loader was actually handed.
If it is red the cause is somewhere else again, and it comes back out with
what it showed recorded.
Requires mcpp:plugins 0.3.0.