Skip to content

examples: the module surface, and one header kept for the contrast - #588

Merged
Sunrisepeak merged 1 commit into
mainfrom
feat/examples-module-surface
Sep 7, 2026
Merged

examples: the module surface, and one header kept for the contrast#588
Sunrisepeak merged 1 commit into
mainfrom
feat/examples-module-surface

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

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.

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 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 rewritten library_path
into the ICD's JSON string unescaped -- C:\Users\... carries \U, which is
not 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 ~/.mcpp
is 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.

@Sunrisepeak
Sunrisepeak force-pushed the feat/examples-module-surface branch 3 times, most recently from 2a968b3 to bbecc48 Compare September 7, 2026 19:44
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
Sunrisepeak force-pushed the feat/examples-module-surface branch from bbecc48 to 3f6f75f Compare September 7, 2026 20:10
@Sunrisepeak
Sunrisepeak merged commit 076b6cc into main Sep 7, 2026
37 checks passed
@Sunrisepeak
Sunrisepeak deleted the feat/examples-module-surface branch September 7, 2026 20:35
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