webgpu sponza
This commit is contained in:
parent
5553ded476
commit
b5d0f52da0
21 changed files with 1426 additions and 58 deletions
166
interfaces/Crafter.Graphics-Image2D.cppm
Normal file
166
interfaces/Crafter.Graphics-Image2D.cppm
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
Crafter®.Graphics
|
||||
Copyright (C) 2026 Catcrafts®
|
||||
catcrafts.net
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License version 3.0 as published by the Free Software Foundation;
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
// Image2D<T> — portable 2D image type whose API surface is intentionally
|
||||
// backend-specific via #ifdef. On Vulkan it aliases the existing
|
||||
// ImageVulkan<T> (full VkFormat / usage / layout control). On WebGPU it's
|
||||
// a thin handle around an rgba8unorm GPUTexture; sizes are u16 and the
|
||||
// only update path is from a CompressedTextureAsset.
|
||||
//
|
||||
// The "no shared no-op signatures" principle is deliberate: callers do
|
||||
// the same #ifdef the library does, and write the backend-specific
|
||||
// invocation. The unified type name Image2D<T> is the only thing
|
||||
// portable between the two — that's the whole point.
|
||||
|
||||
export module Crafter.Graphics:Image2D;
|
||||
|
||||
#ifndef CRAFTER_GRAPHICS_WINDOW_DOM
|
||||
import :ImageVulkan;
|
||||
|
||||
export namespace Crafter {
|
||||
// Vulkan target: Image2D is just the existing ImageVulkan. New name,
|
||||
// same shape — keeps existing ImageVulkan callers (e.g. examples/
|
||||
// Decompression) working without a churn-rename.
|
||||
template <typename PixelType>
|
||||
using Image2D = ImageVulkan<PixelType>;
|
||||
}
|
||||
#endif // !CRAFTER_GRAPHICS_WINDOW_DOM
|
||||
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_DOM
|
||||
import std;
|
||||
import Crafter.Asset;
|
||||
import :DescriptorHeapWebGPU;
|
||||
import :WebGPU;
|
||||
|
||||
export namespace Crafter {
|
||||
template <typename PixelType>
|
||||
class Image2D {
|
||||
public:
|
||||
WebGPUTextureRef handle = 0;
|
||||
std::uint16_t width = 0;
|
||||
std::uint16_t height = 0;
|
||||
|
||||
void Create(std::uint16_t w, std::uint16_t h) {
|
||||
width = w;
|
||||
height = h;
|
||||
handle = WebGPU::wgpuCreateImage2D(w, h);
|
||||
}
|
||||
|
||||
// CPU-decompress the .ctex blob (no GPU decompression on WebGPU)
|
||||
// and upload via wgpuWriteImage2D. The intermediate `pixels` vector
|
||||
// lives only for the duration of this call — the underlying
|
||||
// queue.writeTexture in JS makes its own copy.
|
||||
void Update(const CompressedTextureAsset& asset) {
|
||||
if (asset.pixelStride != sizeof(PixelType)) {
|
||||
std::println(std::cerr,
|
||||
"Image2D::Update: pixel stride mismatch (got {}, expected {})",
|
||||
asset.pixelStride, sizeof(PixelType));
|
||||
std::abort();
|
||||
}
|
||||
std::vector<PixelType> pixels(
|
||||
static_cast<std::size_t>(asset.sizeX) * asset.sizeY);
|
||||
std::array<std::span<std::byte>, 1> outputs = {
|
||||
std::as_writable_bytes(std::span(pixels)),
|
||||
};
|
||||
Compression::DecompressCPU(asset.blob, outputs);
|
||||
WebGPU::wgpuWriteImage2D(
|
||||
handle,
|
||||
pixels.data(),
|
||||
static_cast<std::int32_t>(pixels.size() * sizeof(PixelType)),
|
||||
asset.sizeX, asset.sizeY);
|
||||
}
|
||||
|
||||
// Register the texture in a descriptor heap slot so a custom RT
|
||||
// pipeline can bind it via UICustomBinding::SampledTexture.
|
||||
ImageSlot AllocateSlot(DescriptorHeapWebGPU& heap) {
|
||||
DescriptorRange r = heap.AllocateImageSlots(1);
|
||||
heap.imageTable[r.firstElement] = handle;
|
||||
return ImageSlot(&heap, r.firstElement);
|
||||
}
|
||||
|
||||
void Destroy() {
|
||||
if (handle != 0) {
|
||||
WebGPU::wgpuDestroyTexture(handle);
|
||||
handle = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 2D texture array — `layers` × (w × h) rgba8unorm. Each layer is
|
||||
// populated independently from a CompressedTextureAsset whose dims
|
||||
// must match the array's (w × h). Layer 0 is sampled at array
|
||||
// index 0 in WGSL; bind through UICustomBindingKind::SampledTextureArray.
|
||||
template <typename PixelType>
|
||||
class Image2DArray {
|
||||
public:
|
||||
WebGPUTextureRef handle = 0;
|
||||
std::uint16_t width = 0;
|
||||
std::uint16_t height = 0;
|
||||
std::uint16_t layers = 0;
|
||||
|
||||
void Create(std::uint16_t w, std::uint16_t h, std::uint16_t layerCount) {
|
||||
width = w;
|
||||
height = h;
|
||||
layers = layerCount;
|
||||
handle = WebGPU::wgpuCreateImage2DArray(w, h, layerCount);
|
||||
}
|
||||
|
||||
// Decompress `tex` and upload to `layer`. The asset's dims must
|
||||
// match the array's (w × h) — resize beforehand on the host with
|
||||
// TextureAsset<RGBA8>::Resize() if they don't.
|
||||
void UpdateLayer(std::uint16_t layer, const CompressedTextureAsset& tex) {
|
||||
if (tex.pixelStride != sizeof(PixelType)) {
|
||||
std::println(std::cerr,
|
||||
"Image2DArray::UpdateLayer: pixel stride mismatch (got {}, expected {})",
|
||||
tex.pixelStride, sizeof(PixelType));
|
||||
std::abort();
|
||||
}
|
||||
if (tex.sizeX != width || tex.sizeY != height) {
|
||||
std::println(std::cerr,
|
||||
"Image2DArray::UpdateLayer: layer {} dims {}x{} don't match array dims {}x{}",
|
||||
layer, tex.sizeX, tex.sizeY, width, height);
|
||||
std::abort();
|
||||
}
|
||||
std::vector<PixelType> pixels(static_cast<std::size_t>(width) * height);
|
||||
std::array<std::span<std::byte>, 1> outputs = {
|
||||
std::as_writable_bytes(std::span(pixels)),
|
||||
};
|
||||
Compression::DecompressCPU(tex.blob, outputs);
|
||||
WebGPU::wgpuWriteImage2DLayer(
|
||||
handle, layer,
|
||||
pixels.data(),
|
||||
static_cast<std::int32_t>(pixels.size() * sizeof(PixelType)),
|
||||
width, height);
|
||||
}
|
||||
|
||||
ImageSlot AllocateSlot(DescriptorHeapWebGPU& heap) {
|
||||
DescriptorRange r = heap.AllocateImageSlots(1);
|
||||
heap.imageTable[r.firstElement] = handle;
|
||||
return ImageSlot(&heap, r.firstElement);
|
||||
}
|
||||
|
||||
void Destroy() {
|
||||
if (handle != 0) {
|
||||
WebGPU::wgpuDestroyTexture(handle);
|
||||
handle = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif // CRAFTER_GRAPHICS_WINDOW_DOM
|
||||
Loading…
Add table
Add a link
Reference in a new issue