webgpu embedding
This commit is contained in:
parent
47bd4da0e3
commit
a879c834c7
4 changed files with 86 additions and 4 deletions
|
|
@ -54,6 +54,7 @@ function stub(name) {
|
|||
"wgpuRegisterMeshBLAS", "wgpuRegisterMeshBLASDeviceAabbs", "wgpuRefitMeshBLASDeviceAabbs",
|
||||
"wgpuLoadRTPipeline", "wgpuDispatchRT", "wgpuBuildTLAS",
|
||||
"wgpuLoadComputePipeline", "wgpuDispatchCompute",
|
||||
"wgpuSetCanvasMount",
|
||||
]) {
|
||||
// Read-write ints don't need a stub-throw; return 0 for the size queries.
|
||||
e[n] = n.endsWith("Width") || n.endsWith("Height")
|
||||
|
|
@ -85,6 +86,11 @@ canvas.style.cssText = "position:fixed;inset:0;width:100vw;height:100vh;display:
|
|||
document.body.style.margin = "0";
|
||||
document.body.appendChild(canvas);
|
||||
|
||||
// Optional mount target set via wgpuSetCanvasMount() (below). When null the
|
||||
// canvas is a full-viewport fixed layer (the original, unchanged default);
|
||||
// when set, the canvas is reparented into that element and sized to it.
|
||||
let mountEl = null;
|
||||
|
||||
function syncCanvasSize() {
|
||||
// Canvas pixel size = CSS size × devicePixelRatio so the GPU draws
|
||||
// at physical pixel resolution on HiDPI displays — otherwise the
|
||||
|
|
@ -98,8 +104,18 @@ function syncCanvasSize() {
|
|||
// share the physical-pixel coordinate space with window.width/.height.
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
window.crafter_dpr = dpr;
|
||||
const w = Math.max(1, Math.round(window.innerWidth * dpr));
|
||||
const h = Math.max(1, Math.round(window.innerHeight * dpr));
|
||||
// Mounted → track the host element's box; unmounted → the viewport.
|
||||
let cssW, cssH;
|
||||
if (mountEl) {
|
||||
const r = mountEl.getBoundingClientRect();
|
||||
cssW = r.width;
|
||||
cssH = r.height;
|
||||
} else {
|
||||
cssW = window.innerWidth;
|
||||
cssH = window.innerHeight;
|
||||
}
|
||||
const w = Math.max(1, Math.round(cssW * dpr));
|
||||
const h = Math.max(1, Math.round(cssH * dpr));
|
||||
if (canvas.width !== w) canvas.width = w;
|
||||
if (canvas.height !== h) canvas.height = h;
|
||||
return { w, h };
|
||||
|
|
@ -566,6 +582,40 @@ const env = window.crafter_webbuild_env;
|
|||
env.wgpuGetCanvasWidth = () => canvas.width;
|
||||
env.wgpuGetCanvasHeight = () => canvas.height;
|
||||
|
||||
// Reparent the render canvas into a host DOM element so a Crafter.Graphics
|
||||
// scene can render inline inside an app's own layout instead of as a
|
||||
// full-page layer. `idPtr`/`idLen` is a UTF-8 wasm string:
|
||||
// - non-empty id of an existing element → move the canvas into it,
|
||||
// position it to fill it (absolute inset:0), and size the render
|
||||
// surface to that element's box (syncCanvasSize picks it up per frame).
|
||||
// - empty string → detach back to <body> and hide it (display:none), so
|
||||
// the host page is a plain DOM document again.
|
||||
// The element is made position:relative if it is otherwise static, so the
|
||||
// absolutely-positioned canvas anchors to it. Pointer events pass through
|
||||
// (the inline use cases are non-interactive display surfaces).
|
||||
env.wgpuSetCanvasMount = (idPtr, idLen) => {
|
||||
const id = idLen > 0
|
||||
? new TextDecoder().decode(memU8().subarray(idPtr, idPtr + idLen))
|
||||
: "";
|
||||
if (!id) {
|
||||
mountEl = null;
|
||||
if (canvas.parentNode !== document.body) document.body.appendChild(canvas);
|
||||
canvas.style.cssText = "position:fixed;inset:0;width:100vw;height:100vh;display:none;";
|
||||
return;
|
||||
}
|
||||
const el = document.getElementById(id);
|
||||
if (!el) {
|
||||
console.warn(`[crafter-wgpu] wgpuSetCanvasMount: no element #${id}`);
|
||||
return;
|
||||
}
|
||||
mountEl = el;
|
||||
if (getComputedStyle(el).position === "static") el.style.position = "relative";
|
||||
if (canvas.parentNode !== el) el.appendChild(canvas);
|
||||
canvas.style.cssText =
|
||||
"position:absolute;inset:0;width:100%;height:100%;display:block;pointer-events:none;";
|
||||
ensureSized();
|
||||
};
|
||||
|
||||
env.wgpuCreateBuffer = (byteSize) => {
|
||||
const h = newHandle();
|
||||
const buf = device.createBuffer({
|
||||
|
|
|
|||
|
|
@ -568,6 +568,12 @@ void Device::Initialize() {
|
|||
// https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/12103
|
||||
// Per spencer-lunarg (LunarG) that was fixed in the next SDK release.
|
||||
// The validation layer is now 1.4.350 (> 1.4.341), so re-enable it.
|
||||
// Validation (incl. GPU-Assisted Validation) is a DEVELOPER tool and is OFF
|
||||
// by default: GPU-AV instruments shaders and has crashed vendor SPIR-V
|
||||
// compilers (observed: NVIDIA libnvidia-glvkspirv segfault on the
|
||||
// descriptor-heap compute pipelines). Opt in by setting the environment
|
||||
// variable CRAFTER_GRAPHICS_VALIDATION.
|
||||
bool enableValidation = std::getenv("CRAFTER_GRAPHICS_VALIDATION") != nullptr;
|
||||
VkValidationFeatureEnableEXT enabledValidationFeatures[] = {
|
||||
VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT,
|
||||
};
|
||||
|
|
@ -579,7 +585,7 @@ void Device::Initialize() {
|
|||
|
||||
VkInstanceCreateInfo instanceCreateInfo = {};
|
||||
instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||
instanceCreateInfo.pNext = &validationFeatures;
|
||||
instanceCreateInfo.pNext = enableValidation ? (const void*)&validationFeatures : nullptr;
|
||||
instanceCreateInfo.pApplicationInfo = &app;
|
||||
instanceCreateInfo.enabledExtensionCount = sizeof(instanceExtensionNames) / sizeof(const char*);
|
||||
instanceCreateInfo.ppEnabledExtensionNames = instanceExtensionNames;
|
||||
|
|
@ -603,7 +609,7 @@ void Device::Initialize() {
|
|||
}
|
||||
}
|
||||
|
||||
if (foundInstanceLayers >= sizeof(layerNames) / sizeof(const char*))
|
||||
if (enableValidation && foundInstanceLayers >= sizeof(layerNames) / sizeof(const char*))
|
||||
{
|
||||
instanceCreateInfo.enabledLayerCount = sizeof(layerNames) / sizeof(const char*);
|
||||
instanceCreateInfo.ppEnabledLayerNames = layerNames;
|
||||
|
|
|
|||
|
|
@ -31,6 +31,23 @@ namespace Crafter::WebGPU {
|
|||
__attribute__((import_module("env"), import_name("wgpuInit")))
|
||||
extern "C" void wgpuInit();
|
||||
|
||||
// Reparent the render canvas into a host DOM element so a scene renders
|
||||
// inline inside the app's own layout rather than as a full-page layer.
|
||||
// `idPtr`/`idLen` is a UTF-8 string naming an element id; an empty id
|
||||
// detaches the canvas back to <body> and hides it. Implemented in
|
||||
// additional/dom-webgpu.js. Prefer the SetCanvasMount() wrapper below.
|
||||
__attribute__((import_module("env"), import_name("wgpuSetCanvasMount")))
|
||||
extern "C" void wgpuSetCanvasMount(const void* idPtr, std::int32_t idLen);
|
||||
|
||||
// Mount the render canvas into the DOM element with id `elementId`
|
||||
// (the render surface is sized to that element each frame). Pass an
|
||||
// empty string to detach and hide the canvas, leaving a plain DOM page.
|
||||
// Safe to call any time after the module has started — e.g. from a
|
||||
// route handler once the target element exists in the DOM.
|
||||
export inline void SetCanvasMount(std::string_view elementId) {
|
||||
wgpuSetCanvasMount(elementId.data(), static_cast<std::int32_t>(elementId.size()));
|
||||
}
|
||||
|
||||
__attribute__((import_module("env"), import_name("wgpuCreateBuffer")))
|
||||
extern "C" std::uint32_t wgpuCreateBuffer(std::int32_t byteSize);
|
||||
__attribute__((import_module("env"), import_name("wgpuWriteBuffer")))
|
||||
|
|
|
|||
|
|
@ -6,6 +6,15 @@ using namespace Crafter;
|
|||
extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> args) {
|
||||
std::vector<std::string> depArgs(args.begin(), args.end());
|
||||
|
||||
// Crafter.Event/Math/Asset are always resolved from forgejo (GitProject);
|
||||
// there is no local-sibling branch for them here. A consumer building
|
||||
// THIS project with --local (to pick up local Crafter.Graphics edits)
|
||||
// forwards --local down through depArgs, but these git deps can't honour
|
||||
// it — the flag makes their resolver look for an unsuffixed local
|
||||
// checkout that doesn't exist. Strip it so the sub-deps resolve normally
|
||||
// while Crafter.Graphics itself is still built from the local tree.
|
||||
std::erase(depArgs, "--local");
|
||||
|
||||
Configuration* event = GitProject({
|
||||
.source = { .url = "https://forgejo.catcrafts.net/Catcrafts/Crafter.Event.git" },
|
||||
.args = depArgs,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue