Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mcpp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ members = [
"tests/examples/libdrm",
"tests/examples/libffi",
"tests/examples/expat",
"tests/examples/spirv-headers",
"tests/examples/wayland",
"tests/examples/wayland-egl",
"tests/examples/wayland-cursor",
Expand Down
82 changes: 82 additions & 0 deletions pkgs/c/compat.spirv-headers.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
-- compat.spirv-headers — Khronos SPIR-V headers (the `spirv/unified1` tree).
--
-- Header-only, the same shape as `compat.vulkan-headers`: expose `include/` and
-- carry a trivial anchor TU so the package still produces a buildable lib
-- target.
--
-- WHO NEEDS IT. A program that only submits SPIR-V to a driver does not: the
-- module is opaque data and `vulkan_core.h` describes the API that takes it.
-- A program that READS a SPIR-V module needs the opcode and enum definitions,
-- and llama.cpp's Vulkan backend is one -- it inspects the modules its own
-- shader generator produced. Splitting this out of `compat.vulkan-headers`
-- follows Khronos: they are separate repositories, and a renderer that never
-- decodes SPIR-V should not acquire the decoder's headers.
--
-- THE INCLUDE PATH IS PART OF THE CONTRACT. Consumers spell it
-- `<spirv/unified1/spirv.hpp>`, which is the Khronos layout and what
-- `include/` yields directly. Distributions that flatten it to
-- `<spirv-headers/spirv.hpp>` exist and consumers probe for both with
-- `__has_include`; this package provides the first, so the probe's first arm
-- wins and no consumer needs a second spelling.
--
-- Versioning follows the Vulkan SDK release the tag belongs to
-- (`vulkan-sdk-1.4.357.0` → `1.4.357.0`), which is how Khronos ties the
-- SPIR-V, Vulkan header and loader repositories together.
--
-- LICENCE IS A SET. The bulk is MIT; upstream's own `LICENSE` calls out files
-- under CC-BY-4.0 and ships `LICENSES/` carrying both texts. Naming only the
-- headline would be a statement about the package that its own tree
-- contradicts.
package = {
spec = "1",
namespace = "compat",
name = "spirv-headers",
description = "Khronos SPIR-V headers (spirv/unified1)",
licenses = {"MIT", "CC-BY-4.0"},
repo = "https://github.com/KhronosGroup/SPIRV-Headers",
type = "package",

xpm = {
linux = {
["1.4.357.0"] = {
url = {
GLOBAL = "https://github.com/KhronosGroup/SPIRV-Headers/archive/refs/tags/vulkan-sdk-1.4.357.0.tar.gz",
CN = "https://gitcode.com/mcpp-res/spirv-headers/releases/download/1.4.357.0/spirv-headers-1.4.357.0.tar.gz",
},
sha256 = "4d703067a7e06331ccb37bdfed3f9b7879cc61969a2689ae95c95db34a47ff07",
},
},
macosx = {
["1.4.357.0"] = {
url = {
GLOBAL = "https://github.com/KhronosGroup/SPIRV-Headers/archive/refs/tags/vulkan-sdk-1.4.357.0.tar.gz",
CN = "https://gitcode.com/mcpp-res/spirv-headers/releases/download/1.4.357.0/spirv-headers-1.4.357.0.tar.gz",
},
sha256 = "4d703067a7e06331ccb37bdfed3f9b7879cc61969a2689ae95c95db34a47ff07",
},
},
windows = {
["1.4.357.0"] = {
url = {
GLOBAL = "https://github.com/KhronosGroup/SPIRV-Headers/archive/refs/tags/vulkan-sdk-1.4.357.0.tar.gz",
CN = "https://gitcode.com/mcpp-res/spirv-headers/releases/download/1.4.357.0/spirv-headers-1.4.357.0.tar.gz",
},
sha256 = "4d703067a7e06331ccb37bdfed3f9b7879cc61969a2689ae95c95db34a47ff07",
},
},
},

mcpp = {
language = "c++23",
import_std = false,
c_standard = "c11",
include_dirs = { "*/include" },
generated_files = {
["mcpp_generated/spirv_headers_anchor.c"] =
"int mcpp_compat_spirv_headers_anchor(void) { return 0; }\n",
},
sources = { "mcpp_generated/spirv_headers_anchor.c" },
targets = { ["spirv-headers"] = { kind = "lib" } },
deps = {},
},
}
6 changes: 6 additions & 0 deletions tests/examples/spirv-headers/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "spirv-headers-tests"
version = "0.1.0"

[dependencies.compat]
spirv-headers = "1.4.357.0"
55 changes: 55 additions & 0 deletions tests/examples/spirv-headers/tests/decode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Behavioral test -- verify compat.spirv-headers delivers the Khronos include
// layout and definitions a SPIR-V READER needs.
//
// The criterion is deliberately not "the header compiles". A header-only
// package that resolves and exposes the wrong include root compiles nothing
// and fails at the consumer, so this test spells the include the way a
// consumer spells it and then decodes a module built by hand, which is the
// only thing that can tell a real header tree from an empty one.
#include <spirv/unified1/spirv.hpp>

import std;

int main() {
// A minimal, well-formed SPIR-V module header: magic, version, generator,
// bound, schema. Written as words rather than read from a file so the test
// depends on nothing but the package under test.
const std::uint32_t module_words[] = {
spv::MagicNumber,
spv::Version,
0u, // generator
1u, // bound
0u, // schema
};

if (module_words[0] != 0x07230203u) {
std::println("spv::MagicNumber is {:#x}, expected 0x07230203", module_words[0]);
return 1;
}

// The enums a decoder switches on. Their numeric values are part of the
// SPIR-V specification, so a header that renamed or renumbered them would
// be a different specification rather than a newer package.
if (static_cast<unsigned>(spv::OpCapability) != 17u) {
std::println("spv::OpCapability is {}, expected 17",
static_cast<unsigned>(spv::OpCapability));
return 2;
}
if (static_cast<unsigned>(spv::ExecutionModelGLCompute) != 5u) {
std::println("spv::ExecutionModelGLCompute is {}, expected 5",
static_cast<unsigned>(spv::ExecutionModelGLCompute));
return 3;
}

// The version word packs major and minor into the middle two bytes. A
// consumer that gates on a SPIR-V version reads it exactly this way.
const std::uint32_t major = (module_words[1] >> 16) & 0xffu;
const std::uint32_t minor = (module_words[1] >> 8) & 0xffu;
if (major != 1u) {
std::println("spv::Version reports major {}, expected 1", major);
return 4;
}

std::println("compat.spirv-headers: SPIR-V {}.{} definitions present", major, minor);
return 0;
}
Loading