diff --git a/gltf.wgsl b/gltf.wgsl index 143a678..48ca072 100644 --- a/gltf.wgsl +++ b/gltf.wgsl @@ -5,6 +5,8 @@ struct Configuration { @group(0) @binding(0) var config: Configuration; @group(0) @binding(1) var nodeMatrix: array; +@group(0) @binding(2) var linearSampler: sampler; +@group(0) @binding(3) var colorTexture: texture_2d; /* struct Imm { @@ -24,11 +26,13 @@ struct VertexOutput { @builtin(position) position: vec4f, @location(0) positionWorld: vec3f, @location(1) normal: vec3f, + @location(2) texture: vec2f, }; struct FragmentInput { @location(0) positionWorld: vec3f, @location(1) normal: vec3f, + @location(2) texture: vec2f, }; @vertex @@ -41,6 +45,7 @@ fn vertexMain(input: VertexInput) -> VertexOutput output.position = config.matrix * position; output.positionWorld = (config.matrixWorld * position).xyz; output.normal = (config.matrixWorld * normal).xyz; + output.texture = input.texture; return output; } @@ -88,5 +93,7 @@ fn lighting(position: vec3f, normal: vec3f) -> vec3f fn fragmentMain(input: FragmentInput) -> @location(0) vec4f { let intensity = lighting(input.positionWorld, normalize(input.normal)); - return vec4(intensity, 1.0); + let color = textureSample(colorTexture, linearSampler, input.texture); + + return vec4(color.xyz * intensity, 1.0); } diff --git a/gltf/Image_0.png b/gltf/Image_0.png index 013ec22..901ffb7 100644 Binary files a/gltf/Image_0.png and b/gltf/Image_0.png differ diff --git a/gltf/Image_1.png b/gltf/Image_1.png index eb26f29..b174708 100644 Binary files a/gltf/Image_1.png and b/gltf/Image_1.png differ diff --git a/gltf/Image_2.png b/gltf/Image_2.png index 28ee171..d26850e 100644 Binary files a/gltf/Image_2.png and b/gltf/Image_2.png differ diff --git a/gltf/Image_3.png b/gltf/Image_3.png index e1878ca..2f24ce7 100644 Binary files a/gltf/Image_3.png and b/gltf/Image_3.png differ diff --git a/index.js b/index.js index 304cb5d..4629252 100644 --- a/index.js +++ b/index.js @@ -256,12 +256,14 @@ const rotate = await WebAssembly.instantiateStreaming(fetch("rotate.wasm"), { env: { memory: memory } }); -const memory2 = new WebAssembly.Memory({ initial: 2 }); -const mem = await WebAssembly.instantiateStreaming(fetch("memory.wasm"), { - env: { memory: memory2 } +const memory2 = new WebAssembly.Memory({ initial: 258 * 2 }); +const module = await WebAssembly.instantiateStreaming(fetch("src/module.wasm"), { + env: { + memory: memory2, + log: console.log, + } }); -console.log("mem"); -document.mem = mem; +//document.module = module; const uniformBuffer = device.createBuffer({ label: "grid uniform", @@ -439,7 +441,7 @@ function render() } //requestAnimationFrame(render); -const gltfRenderer = await loadGltf(device, canvasFormat, memory2, mem); +const gltfRenderer = await loadGltf(device, canvasFormat, memory2, module); function render2() { diff --git a/index2.js b/index2.js index 5cdfe3c..c6e6363 100644 --- a/index2.js +++ b/index2.js @@ -2,7 +2,7 @@ import { getPath } from "./common.js"; import { parseGltfChunks, vertexBufferLayouts, primitiveAccessors, splitGltf } from "./gltf.js"; -function loadGltfNode(mem, handle, gltf, nodeIndex) +function loadGltfNode(module, handle, gltf, nodeIndex) { const node = gltf.json.nodes[nodeIndex]; var translation = [0, 0, 0]; @@ -10,20 +10,20 @@ function loadGltfNode(mem, handle, gltf, nodeIndex) translation = node.translation; } console.log("init", nodeIndex, translation); - mem.instance.exports.node_init_translation(handle, nodeIndex, translation[0], translation[1], translation[2]); + module.instance.exports.node_init_translation(handle, nodeIndex, translation[0], translation[1], translation[2]); if ("children" in node) { for (let childIndex in node.children) { - loadGltfNode(mem, handle, gltf, childIndex); + loadGltfNode(module, handle, gltf, childIndex); } } } -function loadGltfNodes(mem, gltf) +function loadGltfNodes(module, gltf) { - const nodesHandle = mem.instance.exports.node_alloc(gltf.json.nodes.length); + const nodesHandle = module.instance.exports.node_alloc(gltf.json.nodes.length); for (let nodeIndex of gltf.json.scenes[0].nodes) { - loadGltfNode(mem, nodesHandle, gltf, nodeIndex); + loadGltfNode(module, nodesHandle, gltf, nodeIndex); } return nodesHandle; } @@ -37,10 +37,17 @@ function printMatrix(m, o) } class GltfRenderer { - constructor(device, canvasFormat, shaderModule, gltf) + constructor(device, canvasFormat, shaderModule, gltf, texture) { this.gltf = gltf; + ////////////////////////////////////////////////////////////////////// + // textures + ////////////////////////////////////////////////////////////////////// + + this.sampler = device.createSampler(); + this.texture = texture; + ////////////////////////////////////////////////////////////////////// // buffer ////////////////////////////////////////////////////////////////////// @@ -75,6 +82,14 @@ class GltfRenderer { binding: 1, visibility: GPUShaderStage.VERTEX, buffer: { type: "read-only-storage" } + }, { + binding: 2, + visibility: GPUShaderStage.FRAGMENT, + sampler: {} + }, { + binding: 3, + visibility: GPUShaderStage.FRAGMENT, + texture: {}, }] }), ]; @@ -162,6 +177,12 @@ class GltfRenderer { }, { binding: 1, resource: { buffer: this.nodeBuffer }, + }, { + binding: 2, + resource: this.sampler, + }, { + binding: 3, + resource: this.texture, }] }), ]; @@ -244,17 +265,102 @@ class GltfRenderer { } } -async function loadGltf(device, canvasFormat, memory, mem) +function memoryString(memory, offset) +{ + const u8 = new Uint8Array(memory.buffer, offset); + + const s = []; + var i = 0; + while (u8[i] != 0 && (i - offset) < 256) { + s.push(u8[i]); + i++; + } + return new TextDecoder().decode(new Uint8Array(s)); +} + +function pngComponents(colorType) +{ + if (colorType == 2) + return 3; + if (colorType == 6) + return 4; + throw new Error("non-implemented png color type"); +} + +async function loadTextures(device, memory, module, gltf) +{ + const tempTable = module.instance.exports.mem_alloc(256 * 4); + const tempHeader = module.instance.exports.mem_alloc(7 * 4); + + const tempInputSize = 8 * 1024 * 1024; + const tempInput = module.instance.exports.mem_alloc(tempInputSize); + + const tempOutputSize = 2048 * 2048 * 4; + const tempOutput = module.instance.exports.mem_alloc(tempOutputSize); + + const tempError = module.instance.exports.mem_alloc(4); + + module.instance.exports.png_build_crc_table(tempTable); + + const tempInputU8 = new Uint8Array(memory.buffer, tempInput); + const tempOutputU8 = new Uint8Array(memory.buffer, tempOutput); + const tempErrorU32 = new Uint32Array(memory.buffer, tempError); + const tempHeaderU32 = new Uint32Array(memory.buffer, tempHeader); + + const textures = []; + for (let image of gltf.json.images) { + console.log(image.uri); + const imageResponse = await fetch(`gltf/${image.uri}`); + const imageBuffer = await imageResponse.arrayBuffer(); + const imageBufferU8 = new Uint8Array(imageBuffer) + + tempInputU8.set(imageBufferU8); + const ret = module.instance.exports.png_decode(tempTable, tempInput, imageBuffer.byteLength, + tempHeader, + tempOutput, tempOutputSize, + tempError); + if (ret < 0) { + console.log("png ret", ret, memoryString(memory, tempErrorU32[0])); + throw new Error("png error"); + } + + const width = tempHeaderU32[0]; + const height = tempHeaderU32[1]; + const bitDepth = tempHeaderU32[2]; + const colorType = tempHeaderU32[3]; + const components = pngComponents(colorType); + console.log(components); + + const texture = device.createTexture({ + size: [width, height], + format: 'rgba8unorm', + usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST, + }); + + device.queue.writeTexture({ texture: texture }, + tempOutputU8, + { bytesPerRow: width * components }, + { width: width, height: height }); + + textures.push(texture); + } + return textures; +} + +async function loadGltf(device, canvasFormat, memory, module) { const gltf = await splitGltf("gltf/lamp.gltf", "gltf/lamp.bin"); - const nodesHandle = loadGltfNodes(mem, gltf); + const nodesHandle = loadGltfNodes(module, gltf); + /* const nodesM = new Float32Array(memory.buffer, nodesHandle); for (let i = 0; i < gltf.json.nodes.length; i++) { console.log("[", i, "]"); printMatrix(nodesM, i * 16); } + */ + const textures = await loadTextures(device, memory, module, gltf); const code = await getPath("gltf.wgsl"); const shaderModule = device.createShaderModule({ @@ -262,7 +368,7 @@ async function loadGltf(device, canvasFormat, memory, mem) code: await code, }); - const renderer = new GltfRenderer(device, canvasFormat, shaderModule, gltf, nodesHandle); + const renderer = new GltfRenderer(device, canvasFormat, shaderModule, gltf, textures[2]); console.log(renderer.nodeBuffer); device.queue.writeBuffer(renderer.nodeBuffer, 0, memory.buffer, nodesHandle, (4 * 4 * 4) * gltf.json.nodes.length); diff --git a/memory.cpp b/memory.cpp deleted file mode 100644 index ca0f0d7..0000000 --- a/memory.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include - -#include "directxmath/directxmath.h" - -//size_t __builtin_wasm_memory_grow(int, size_t); -//size_t __builtin_wasm_memory_size(int); - -extern char __heap_base[]; -extern char __heap_end[]; - -static size_t heap_pages = 0; -static size_t alloc_index = 0; - -extern "C" { - size_t mem_size(); - size_t mem_alloc(size_t length); - size_t node_alloc(int count); - - void node_init_translation(size_t handle, int index, float x, float y, float z); -}; - -static inline size_t memory_heap_base() -{ - return (size_t)&__heap_base[0]; -} - -static inline size_t align(size_t n, size_t m) -{ - return (n + (m - 1)) & (~(m - 1)); -} - -static inline void memory_grow(size_t npages) -{ - __builtin_wasm_memory_grow(0, npages); -} - -size_t mem_size() -{ - return __builtin_wasm_memory_size(0); -} - -size_t mem_alloc(size_t length) -{ - size_t base = memory_heap_base(); - - size_t heap_end = (heap_pages << 16); - - size_t address = alloc_index; - - alloc_index += align(length, 16); - - if (heap_end < alloc_index) { - size_t npages = align(alloc_index - heap_end, 65536) >> 16; - memory_grow(npages); - heap_pages += npages; - } - return base + address; -} - -size_t node_alloc(int count) -{ - return mem_alloc((sizeof (XMFLOAT4X4)) * count); -} - -void node_init_translation(size_t handle, int index, float x, float y, float z) -{ - XMFLOAT4X4 * nodes = reinterpret_cast(handle); - //XMMATRIX XMLoadFloat4x4(&nodes[index]); - XMMATRIX mat = XMMatrixTranslation(x, y, z); - XMStoreFloat4x4(&nodes[index], mat); -} diff --git a/src/Makefile b/src/Makefile index e8c1b78..74f0455 100644 --- a/src/Makefile +++ b/src/Makefile @@ -3,14 +3,16 @@ MINIZ = $(HOME)/miniz CFLAGS = \ --target=wasm32 \ -g \ - -O0 \ + -O3 \ + -flto \ -nostdlib \ -I$(MINIZ) \ + -I.. \ -I. \ -Werror \ - -Wfatal-errors + -Wfatal-errors \ + -D_XM_NO_INTRINSICS_ -# -flto \ MFLAGS = \ -DMINIZ_NO_STDIO \ @@ -19,15 +21,18 @@ MFLAGS = \ -DMINIZ_NO_ZLIB_APIS \ -DMINIZ_NO_MALLOC -# -Wl,--lto-O1 \ - LDFLAGS = \ -Wl,--no-entry \ - -Wl,--export=build_crc_table \ - -Wl,--export=decode \ + -Wl,--export=mem_size \ + -Wl,--export=mem_alloc \ + -Wl,--export=node_alloc \ + -Wl,--export=node_init_translation \ + -Wl,--export=png_build_crc_table \ + -Wl,--export=png_decode \ + -Wl,--import-undefined \ -Wl,--print-map \ -Wl,--import-memory \ - -Wl,-z,stack-size=1048576 + -Wl,--lto-O3 %.o: %.c clang $(CFLAGS) $(MFLAGS) -o $@ -c $< @@ -38,7 +43,9 @@ LDFLAGS = \ PNG_OBJ = \ png.o \ stdlib.o \ - $(MINIZ)/miniz_tinfl.o + $(MINIZ)/miniz_tinfl.o \ + memory.o \ + node.o -png.wasm: $(PNG_OBJ) +module.wasm: $(PNG_OBJ) clang++ $(CFLAGS) $(LDFLAGS) -o $@ $^ diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..d63d9b7 --- /dev/null +++ b/src/index.html @@ -0,0 +1,14 @@ + + + + + + WebGPU + + + + + + + diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..5177cf6 --- /dev/null +++ b/src/index.js @@ -0,0 +1,124 @@ +const canvas = document.querySelector("canvas"); +canvas.width = 480; +canvas.height = 480; + +const context = canvas.getContext("2d"); +const imageData = context.createImageData(canvas.width, canvas.height); + +const cstride = canvas.width * 4; + +for (let y = 0; y < canvas.height; y++) { + for (let x = 0; x < canvas.width; x++) { + imageData.data[y * cstride + x * 4 + 0] = x; + imageData.data[y * cstride + x * 4 + 1] = y; + imageData.data[y * cstride + x * 4 + 2] = 0; + imageData.data[y * cstride + x * 4 + 3] = 255; + } +} +context.putImageData(imageData, 0, 0); + +const memoryBlocks = 129 + 128 * 10; +const memorySize = memoryBlocks * (64 * 1024); +const memory = new WebAssembly.Memory({ initial: memoryBlocks }); +const memoryU8 = new Uint8Array(memory.buffer); + +function round(n, m) +{ + return (n + (m - 1)) & ~(m - 1); +} + +class Allocator { + constructor(size) { + this.size = size; + this.offset = 0; + } + + alloc(size) { + const address = this.offset; + this.offset += round(size, 32); + if (this.offset > this.size) { + throw new Error(`allocator overflow: ${this.offset} > ${this.size}`); + } + return address; + } +} + +function memoryString(mem, offset) +{ + const s = []; + var i = offset; + while (mem[i] != 0 && (i - offset) < 256) { + s.push(mem[i]); + i++; + } + return new TextDecoder().decode(new Uint8Array(s)); +} + +const m = new Allocator(memorySize); +m.offset = (129 + 64) * 64 * 1024; +const crcTableA = m.alloc(256 * 4); + +const png = await WebAssembly.instantiateStreaming(fetch("png.wasm"), { + env: { memory: memory } +}); + +/* +const test = await WebAssembly.instantiateStreaming(fetch("test.wasm"), { + env: { memory: memory } + }); +const res = test.instance.exports.test(); +console.log("res", res); +console.log(memoryString(memoryU8, res)); +*/ + +png.instance.exports.build_crc_table(crcTableA); +/* +const crcTable = new Uint32Array(memory.buffer, crcTableA); +for (let i = 0; i < 256; i++) { + console.log(crcTable[i].toString(16).padStart(8, '0')); +} +*/ + +const pngResponse = await fetch("../gltf/Image_0.png"); +const pngBuffer = await pngResponse.arrayBuffer(); +console.log(pngBuffer.byteLength); + +const pngA = m.alloc(pngBuffer.byteLength); +const pngBufferU8 = new Uint8Array(pngBuffer); +memoryU8.set(pngBufferU8, pngA); +console.log("a0", memoryU8[pngA], pngBufferU8[0]); + +const headerA = m.alloc(7 * 4); + +const obufSize = canvas.width * canvas.height * 3; +const obufA = m.alloc(obufSize); +console.log("obufA", obufA, obufSize); + +const errorA = m.alloc(4); + +const ret = png.instance.exports.decode(crcTableA, pngA, pngBuffer.byteLength, + headerA, + obufA, obufSize, + errorA); +if (ret < 0) { + const address = new Uint32Array(memory.buffer, errorA); + console.log("error3", ret, crcTableA, address[0], memoryString(memoryU8, address[0])); + //for (let i = 0; i < 256; i++) { + //console.log(memoryU8[(-ret) + i]); + //} +} +console.log(ret); +if (ret >= 0) { + console.log("ret", ret); + const pstride = canvas.width * 3; + + for (let y = 0; y < canvas.height; y++) { + for (let x = 0; x < canvas.width; x++) { + imageData.data[y * cstride + x * 4 + 0] = memoryU8[obufA + pstride * y + x * 3 + 0]; + imageData.data[y * cstride + x * 4 + 1] = memoryU8[obufA + pstride * y + x * 3 + 1]; + imageData.data[y * cstride + x * 4 + 2] = memoryU8[obufA + pstride * y + x * 3 + 2]; + imageData.data[y * cstride + x * 4 + 3] = 255; + } + } + context.putImageData(imageData, 0, 0); +} diff --git a/src/memory.cpp b/src/memory.cpp new file mode 100644 index 0000000..89fb2c7 --- /dev/null +++ b/src/memory.cpp @@ -0,0 +1,47 @@ +#include + +#include "memory.h" + +extern char __heap_base[]; +extern char __heap_end[]; + +static size_t heap_pages = 0; +static size_t alloc_index = 0; + +static inline size_t memory_heap_base() +{ + return (size_t)&__heap_end[0]; +} + +static inline size_t align(size_t n, size_t m) +{ + return (n + (m - 1)) & (~(m - 1)); +} + +static inline void memory_grow(size_t npages) +{ + __builtin_wasm_memory_grow(0, npages); +} + +size_t mem_size() +{ + return __builtin_wasm_memory_size(0); +} + +size_t mem_alloc(size_t length) +{ + size_t base = memory_heap_base(); + + size_t heap_end = (heap_pages << 16); + + alloc_index = align(alloc_index, 16); + size_t address = alloc_index; + alloc_index += length; + + if (heap_end < alloc_index) { + size_t npages = align(alloc_index - heap_end, 65536) >> 16; + memory_grow(npages); + heap_pages += npages; + } + return base + address; +} diff --git a/src/memory.h b/src/memory.h new file mode 100644 index 0000000..b760cd5 --- /dev/null +++ b/src/memory.h @@ -0,0 +1,6 @@ +#pragma once + +extern "C" { + size_t mem_size(); + size_t mem_alloc(size_t length); +}; diff --git a/src/node.cpp b/src/node.cpp new file mode 100644 index 0000000..89ce8f7 --- /dev/null +++ b/src/node.cpp @@ -0,0 +1,19 @@ +#include + +#include "memory.h" +#include "node.h" + +#include "directxmath/directxmath.h" + +size_t node_alloc(int count) +{ + return mem_alloc((sizeof (XMFLOAT4X4)) * count); +} + +void node_init_translation(size_t handle, int index, float x, float y, float z) +{ + XMFLOAT4X4 * nodes = reinterpret_cast(handle); + //XMMATRIX XMLoadFloat4x4(&nodes[index]); + XMMATRIX mat = XMMatrixTranslation(x, y, z); + XMStoreFloat4x4(&nodes[index], mat); +} diff --git a/src/node.h b/src/node.h new file mode 100644 index 0000000..73a2b1b --- /dev/null +++ b/src/node.h @@ -0,0 +1,6 @@ +#pragma once + +extern "C" { + size_t node_alloc(int count); + void node_init_translation(size_t handle, int index, float x, float y, float z); +}; diff --git a/src/png.cpp b/src/png.cpp index 453473a..4966873 100644 --- a/src/png.cpp +++ b/src/png.cpp @@ -9,6 +9,7 @@ #include #endif +#include "png.h" #include "miniz_tinfl.h" struct CT { @@ -33,7 +34,7 @@ struct Header { int interlace_method; }; -#define IBUF_SIZE ((1024 + 1) * 1024 * 4) +#define IBUF_SIZE ((2048 + 1) * 2048 * 4) struct png_state { tinfl_decompressor decomp; @@ -45,9 +46,7 @@ struct png_state { static png_state _png_state; -extern "C" void build_crc_table(uint32_t * crc_table); - -void build_crc_table(uint32_t * crc_table) +void png_build_crc_table(uint32_t * crc_table) { for (uint32_t i = 0; i < 256; i++) { uint32_t c = i; @@ -269,8 +268,8 @@ static int decode_chunk(uint32_t const * crc_table, uint8_t const * mem, int & o offset += length; uint32_t e_crc = decode_int32be(mem, offset); if (e_crc != a_crc) { - state->error = "chunk crc mismatch"; - return -5; + state->error = "chunk crc mismatch2"; + return -offset; } offset += 4; @@ -285,15 +284,10 @@ static int decode_chunk(uint32_t const * crc_table, uint8_t const * mem, int & o } } -extern "C" int decode(uint32_t const * crc_table, uint8_t const * mem, int length, - Header * header, - uint8_t * obuf, int osize, - char const ** error); - -int decode(uint32_t const * crc_table, uint8_t const * mem, int length, - Header * header, - uint8_t * obuf, int osize, - char const ** error) +int png_decode(uint32_t const * crc_table, uint8_t const * mem, int length, + Header * header, + uint8_t * obuf, int osize, + char const ** error) { *error = 0; tinfl_init(&_png_state.decomp); @@ -376,22 +370,22 @@ int main(int argc, char const ** argv) void * ibuf = read_file(argv[1], &isize); uint32_t crc_table[256]; - build_crc_table(crc_table); + png_build_crc_table(crc_table); uint8_t mem[] = {0x49, 0x45, 0x4E, 0x44}; uint32_t crc = calculate_crc(crc_table, mem, 0, 4); assert(crc == 0xae426082); - int osize = 8 * 1024 * 1024; + int osize = 8 * 2048 * 2048; void * obuf = malloc(osize); Header header{}; char const * error; - int ret = decode(crc_table, - reinterpret_cast(ibuf), isize, - &header, - reinterpret_cast(obuf), osize, - &error); + int ret = png_decode(crc_table, + reinterpret_cast(ibuf), isize, + &header, + reinterpret_cast(obuf), osize, + &error); if (ret < 0) return 1; diff --git a/src/png.h b/src/png.h new file mode 100644 index 0000000..861ee3b --- /dev/null +++ b/src/png.h @@ -0,0 +1,11 @@ +#pragma once + +struct Header; + +extern "C" { + void png_build_crc_table(uint32_t * crc_table); + int png_decode(uint32_t const * crc_table, uint8_t const * mem, int length, + Header * header, + uint8_t * obuf, int osize, + char const ** error); +}