@bloopjs/toodle 0.0.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +44 -0
- package/dist/Toodle.d.ts +304 -0
- package/dist/Toodle.d.ts.map +1 -0
- package/dist/colors/mod.d.ts +872 -0
- package/dist/colors/mod.d.ts.map +1 -0
- package/dist/coreTypes/Color.d.ts +7 -0
- package/dist/coreTypes/Color.d.ts.map +1 -0
- package/dist/coreTypes/Point.d.ts +8 -0
- package/dist/coreTypes/Point.d.ts.map +1 -0
- package/dist/coreTypes/Size.d.ts +5 -0
- package/dist/coreTypes/Size.d.ts.map +1 -0
- package/dist/coreTypes/Transform.d.ts +16 -0
- package/dist/coreTypes/Transform.d.ts.map +1 -0
- package/dist/coreTypes/Vec2.d.ts +8 -0
- package/dist/coreTypes/Vec2.d.ts.map +1 -0
- package/dist/coreTypes/mod.d.ts +6 -0
- package/dist/coreTypes/mod.d.ts.map +1 -0
- package/dist/docs/snippets/add-remove-children.d.ts +1 -0
- package/dist/docs/snippets/basic-quad.d.ts +1 -0
- package/dist/docs/snippets/filter-linear.d.ts +1 -0
- package/dist/docs/snippets/filter-nearest.d.ts +1 -0
- package/dist/docs/snippets/flipxy.d.ts +1 -0
- package/dist/docs/snippets/hello-text.d.ts +1 -0
- package/dist/docs/snippets/jumbo-textures.d.ts +1 -0
- package/dist/docs/snippets/layer.d.ts +1 -0
- package/dist/docs/snippets/layout-edges.d.ts +1 -0
- package/dist/docs/snippets/layout-screen-and-world-space.d.ts +1 -0
- package/dist/docs/snippets/postprocess.d.ts +1 -0
- package/dist/docs/snippets/quad-size-scale.d.ts +1 -0
- package/dist/docs/snippets/quickstart.d.ts +1 -0
- package/dist/docs/snippets/repeat-texture-loading.d.ts +1 -0
- package/dist/docs/snippets/screen-shaders.d.ts +1 -0
- package/dist/docs/snippets/shader-color-flash.d.ts +1 -0
- package/dist/docs/snippets/shader-default.d.ts +1 -0
- package/dist/docs/snippets/shader-fill.d.ts +1 -0
- package/dist/docs/snippets/shapes-line.d.ts +1 -0
- package/dist/docs/snippets/sprite-region.d.ts +1 -0
- package/dist/docs/snippets/text-alignment.d.ts +1 -0
- package/dist/docs/snippets/text-shrink-to-fit.d.ts +1 -0
- package/dist/docs/snippets/text-word-wrap.d.ts +1 -0
- package/dist/docs/snippets/texture-bundles-prebaked.d.ts +1 -0
- package/dist/docs/snippets/texture-bundles.d.ts +1 -0
- package/dist/docs/snippets/transforms.d.ts +1 -0
- package/dist/docs/snippets/transparent-cropping.d.ts +1 -0
- package/dist/examples/0-hello.d.ts +1 -0
- package/dist/examples/1-hello.d.ts +1 -0
- package/dist/examples/1-hello.d.ts.map +1 -0
- package/dist/examples/1-quad.d.ts +1 -0
- package/dist/examples/10-resize.d.ts +1 -0
- package/dist/examples/10-resize.d.ts.map +1 -0
- package/dist/examples/11-bundle-test.d.ts +1 -0
- package/dist/examples/11-bundle-test.d.ts.map +1 -0
- package/dist/examples/12-transparent-pixel-cropping.d.ts +1 -0
- package/dist/examples/12-transparent-pixel-cropping.d.ts.map +1 -0
- package/dist/examples/13-crop.d.ts +1 -0
- package/dist/examples/13-crop.d.ts.map +1 -0
- package/dist/examples/14-bundle-bench.d.ts +1 -0
- package/dist/examples/14-bundle-bench.d.ts.map +1 -0
- package/dist/examples/15-text-layer.d.ts +1 -0
- package/dist/examples/15-text-layer.d.ts.map +1 -0
- package/dist/examples/16-jumbo-texture.d.ts +1 -0
- package/dist/examples/16-jumbo-textures.d.ts +1 -0
- package/dist/examples/16-screen-shader.d.ts +1 -0
- package/dist/examples/16-screen-shader.d.ts.map +1 -0
- package/dist/examples/17-lighting.d.ts +1 -0
- package/dist/examples/17-lighting.d.ts.map +1 -0
- package/dist/examples/17-translations.d.ts +1 -0
- package/dist/examples/18-blur.d.ts +1 -0
- package/dist/examples/19-postprocess.d.ts +1 -0
- package/dist/examples/19-screenshader.d.ts +1 -0
- package/dist/examples/2-shapes.d.ts +1 -0
- package/dist/examples/2-shapes.d.ts.map +1 -0
- package/dist/examples/3-shader.d.ts +1 -0
- package/dist/examples/3-shader.d.ts.map +1 -0
- package/dist/examples/4-shader-bench.d.ts +1 -0
- package/dist/examples/4-shader-bench.d.ts.map +1 -0
- package/dist/examples/5-z.d.ts +1 -0
- package/dist/examples/5-z.d.ts.map +1 -0
- package/dist/examples/6-atlas.d.ts +1 -0
- package/dist/examples/6-atlas.d.ts.map +1 -0
- package/dist/examples/7-text.d.ts +1 -0
- package/dist/examples/7-text.d.ts.map +1 -0
- package/dist/examples/8-text-bench.d.ts +1 -0
- package/dist/examples/8-text-bench.d.ts.map +1 -0
- package/dist/examples/9-alignment.d.ts +1 -0
- package/dist/examples/9-alignment.d.ts.map +1 -0
- package/dist/examples/main.d.ts +1 -0
- package/dist/examples/main.d.ts.map +1 -0
- package/dist/examples/util.d.ts +82 -0
- package/dist/examples/util.d.ts.map +1 -0
- package/dist/limits.d.ts +23 -0
- package/dist/limits.d.ts.map +1 -0
- package/dist/math/angle.d.ts +13 -0
- package/dist/math/angle.d.ts.map +1 -0
- package/dist/math/matrix.d.ts +26 -0
- package/dist/math/matrix.d.ts.map +1 -0
- package/dist/math/mod.d.ts +3 -0
- package/dist/math/mod.d.ts.map +1 -0
- package/dist/mod.d.ts +17 -0
- package/dist/mod.d.ts.map +1 -0
- package/dist/mod.js +19665 -0
- package/dist/mod.js.map +41 -0
- package/dist/postprocess.d.ts +10 -0
- package/dist/postprocess.d.ts.map +1 -0
- package/dist/scene/Batcher.d.ts +20 -0
- package/dist/scene/Batcher.d.ts.map +1 -0
- package/dist/scene/Camera.d.ts +16 -0
- package/dist/scene/Camera.d.ts.map +1 -0
- package/dist/scene/JumboQuadNode.d.ts +29 -0
- package/dist/scene/JumboQuadNode.d.ts.map +1 -0
- package/dist/scene/QuadNode.d.ts +159 -0
- package/dist/scene/QuadNode.d.ts.map +1 -0
- package/dist/scene/RenderComponent.d.ts +11 -0
- package/dist/scene/RenderComponent.d.ts.map +1 -0
- package/dist/scene/SceneNode.d.ts +300 -0
- package/dist/scene/SceneNode.d.ts.map +1 -0
- package/dist/scene/mod.d.ts +5 -0
- package/dist/scene/mod.d.ts.map +1 -0
- package/dist/screen/mod.d.ts +2 -0
- package/dist/screen/mod.d.ts.map +1 -0
- package/dist/screen/resolution.d.ts +5 -0
- package/dist/screen/resolution.d.ts.map +1 -0
- package/dist/shaders/EngineUniform.d.ts +9 -0
- package/dist/shaders/EngineUniform.d.ts.map +1 -0
- package/dist/shaders/IShader.d.ts +15 -0
- package/dist/shaders/IShader.d.ts.map +1 -0
- package/dist/shaders/QuadShader.d.ts +18 -0
- package/dist/shaders/QuadShader.d.ts.map +1 -0
- package/dist/shaders/ShaderDescriptor.d.ts +7 -0
- package/dist/shaders/ShaderDescriptor.d.ts.map +1 -0
- package/dist/shaders/mod.d.ts +6 -0
- package/dist/shaders/mod.d.ts.map +1 -0
- package/dist/shaders/parser.d.ts +8 -0
- package/dist/shaders/parser.d.ts.map +1 -0
- package/dist/shaders/postprocess/blur.d.ts +3 -0
- package/dist/shaders/postprocess/blur.d.ts.map +1 -0
- package/dist/shaders/postprocess/mod.d.ts +17 -0
- package/dist/shaders/postprocess/mod.d.ts.map +1 -0
- package/dist/shaders/samplers.d.ts +3 -0
- package/dist/shaders/samplers.d.ts.map +1 -0
- package/dist/shaders/wgsl/example.wgsl.d.ts +3 -0
- package/dist/shaders/wgsl/example.wgsl.d.ts.map +1 -0
- package/dist/shaders/wgsl/hello.wgsl.d.ts +3 -0
- package/dist/shaders/wgsl/hello.wgsl.d.ts.map +1 -0
- package/dist/shaders/wgsl/helloInstanced.wgsl.d.ts +3 -0
- package/dist/shaders/wgsl/helloInstanced.wgsl.d.ts.map +1 -0
- package/dist/shaders/wgsl/quad.wgsl.d.ts +3 -0
- package/dist/shaders/wgsl/quad.wgsl.d.ts.map +1 -0
- package/dist/src/Toodle.d.ts +303 -0
- package/dist/src/Toodle.d.ts.map +1 -0
- package/dist/src/colors/mod.d.ts +871 -0
- package/dist/src/coreTypes/Color.d.ts +6 -0
- package/dist/src/coreTypes/Color.d.ts.map +1 -0
- package/dist/src/coreTypes/Point.d.ts +7 -0
- package/dist/src/coreTypes/Point.d.ts.map +1 -0
- package/dist/src/coreTypes/Size.d.ts +4 -0
- package/dist/src/coreTypes/Size.d.ts.map +1 -0
- package/dist/src/coreTypes/Transform.d.ts +15 -0
- package/dist/src/coreTypes/Transform.d.ts.map +1 -0
- package/dist/src/coreTypes/Vec2.d.ts +7 -0
- package/dist/src/coreTypes/Vec2.d.ts.map +1 -0
- package/dist/src/coreTypes/mod.d.ts +5 -0
- package/dist/src/coreTypes/mod.d.ts.map +1 -0
- package/dist/src/limits.d.ts +22 -0
- package/dist/src/limits.d.ts.map +1 -0
- package/dist/src/math/angle.d.ts +12 -0
- package/dist/src/math/angle.d.ts.map +1 -0
- package/dist/src/math/matrix.d.ts +25 -0
- package/dist/src/math/matrix.d.ts.map +1 -0
- package/dist/src/math/mod.d.ts +2 -0
- package/dist/src/math/mod.d.ts.map +1 -0
- package/dist/src/mod.d.ts +16 -0
- package/dist/src/mod.d.ts.map +1 -0
- package/dist/src/postprocess.d.ts +10 -0
- package/dist/src/postprocess.d.ts.map +1 -0
- package/dist/src/scene/Batcher.d.ts +19 -0
- package/dist/src/scene/Batcher.d.ts.map +1 -0
- package/dist/src/scene/Camera.d.ts +15 -0
- package/dist/src/scene/Camera.d.ts.map +1 -0
- package/dist/src/scene/JumboQuadNode.d.ts +28 -0
- package/dist/src/scene/QuadNode.d.ts +158 -0
- package/dist/src/scene/QuadNode.d.ts.map +1 -0
- package/dist/src/scene/RenderComponent.d.ts +10 -0
- package/dist/src/scene/RenderComponent.d.ts.map +1 -0
- package/dist/src/scene/SceneNode.d.ts +299 -0
- package/dist/src/scene/SceneNode.d.ts.map +1 -0
- package/dist/src/scene/mod.d.ts +4 -0
- package/dist/src/scene/mod.d.ts.map +1 -0
- package/dist/src/screen/mod.d.ts +1 -0
- package/dist/src/screen/mod.d.ts.map +1 -0
- package/dist/src/screen/resolution.d.ts +4 -0
- package/dist/src/screen/resolution.d.ts.map +1 -0
- package/dist/src/shaders/EngineUniform.d.ts +8 -0
- package/dist/src/shaders/EngineUniform.d.ts.map +1 -0
- package/dist/src/shaders/IShader.d.ts +14 -0
- package/dist/src/shaders/IShader.d.ts.map +1 -0
- package/dist/src/shaders/QuadShader.d.ts +17 -0
- package/dist/src/shaders/QuadShader.d.ts.map +1 -0
- package/dist/src/shaders/ShaderDescriptor.d.ts +6 -0
- package/dist/src/shaders/ShaderDescriptor.d.ts.map +1 -0
- package/dist/src/shaders/mod.d.ts +5 -0
- package/dist/src/shaders/mod.d.ts.map +1 -0
- package/dist/src/shaders/parser.d.ts +7 -0
- package/dist/src/shaders/parser.d.ts.map +1 -0
- package/dist/src/shaders/postprocess/blur.d.ts +2 -0
- package/dist/src/shaders/postprocess/mod.d.ts +16 -0
- package/dist/src/shaders/postprocess/postprocess.d.ts +8 -0
- package/dist/src/shaders/postprocess/util.d.ts +2 -0
- package/dist/src/shaders/samplers.d.ts +2 -0
- package/dist/src/shaders/samplers.d.ts.map +1 -0
- package/dist/src/shaders/wgsl/example.wgsl.d.ts +2 -0
- package/dist/src/shaders/wgsl/example.wgsl.d.ts.map +1 -0
- package/dist/src/shaders/wgsl/hello.wgsl.d.ts +2 -0
- package/dist/src/shaders/wgsl/hello.wgsl.d.ts.map +1 -0
- package/dist/src/shaders/wgsl/helloInstanced.wgsl.d.ts +2 -0
- package/dist/src/shaders/wgsl/helloInstanced.wgsl.d.ts.map +1 -0
- package/dist/src/shaders/wgsl/quad.wgsl.d.ts +2 -0
- package/dist/src/shaders/wgsl/quad.wgsl.d.ts.map +1 -0
- package/dist/src/text/FontPipeline.d.ts +13 -0
- package/dist/src/text/FontPipeline.d.ts.map +1 -0
- package/dist/src/text/MsdfFont.d.ts +81 -0
- package/dist/src/text/MsdfFont.d.ts.map +1 -0
- package/dist/src/text/TextFormatting.d.ts +18 -0
- package/dist/src/text/TextFormatting.d.ts.map +1 -0
- package/dist/src/text/TextNode.d.ts +18 -0
- package/dist/src/text/TextNode.d.ts.map +1 -0
- package/dist/src/text/TextShader.d.ts +14 -0
- package/dist/src/text/TextShader.d.ts.map +1 -0
- package/dist/src/text/mod.d.ts +3 -0
- package/dist/src/text/mod.d.ts.map +1 -0
- package/dist/src/text/shaping.d.ts +38 -0
- package/dist/src/text/shaping.d.ts.map +1 -0
- package/dist/src/text/text.wgsl.d.ts +2 -0
- package/dist/src/text/text.wgsl.d.ts.map +1 -0
- package/dist/src/textures/AssetManager.d.ts +181 -0
- package/dist/src/textures/AssetManager.d.ts.map +1 -0
- package/dist/src/textures/NewTextureComputeShader.d.ts +28 -0
- package/dist/src/textures/TextureComputeShader.d.ts +20 -0
- package/dist/src/textures/TextureComputeShader.d.ts.map +1 -0
- package/dist/src/textures/crop.wgsl.d.ts +2 -0
- package/dist/src/textures/mod.d.ts +1 -0
- package/dist/src/textures/mod.d.ts.map +1 -0
- package/dist/src/textures/pixel-scraping.wgsl.d.ts +2 -0
- package/dist/src/textures/pixel-scraping.wgsl.d.ts.map +1 -0
- package/dist/src/textures/texture-processing.wgsl.d.ts +2 -0
- package/dist/src/textures/types.d.ts +176 -0
- package/dist/src/textures/types.d.ts.map +1 -0
- package/dist/src/textures/util.d.ts +7 -0
- package/dist/src/textures/util.d.ts.map +1 -0
- package/dist/src/utils/assert.d.ts +1 -0
- package/dist/src/utils/assert.d.ts.map +1 -0
- package/dist/src/utils/boilerplate.d.ts +10 -0
- package/dist/src/utils/boilerplate.d.ts.map +1 -0
- package/dist/src/utils/error.d.ts +7 -0
- package/dist/src/utils/error.d.ts.map +1 -0
- package/dist/src/utils/mod.d.ts +2 -0
- package/dist/src/utils/mod.d.ts.map +1 -0
- package/dist/src/utils/pool.d.ts +22 -0
- package/dist/src/utils/pool.d.ts.map +1 -0
- package/dist/test/math/matrix.test.d.ts +1 -0
- package/dist/test/scene/Batcher.test.d.ts +1 -0
- package/dist/test/scene/SceneNode.test.d.ts +1 -0
- package/dist/test/shader/parser.test.d.ts +1 -0
- package/dist/text/FontPipeline.d.ts +14 -0
- package/dist/text/FontPipeline.d.ts.map +1 -0
- package/dist/text/MsdfFont.d.ts +82 -0
- package/dist/text/MsdfFont.d.ts.map +1 -0
- package/dist/text/TextFormatting.d.ts +19 -0
- package/dist/text/TextFormatting.d.ts.map +1 -0
- package/dist/text/TextNode.d.ts +19 -0
- package/dist/text/TextNode.d.ts.map +1 -0
- package/dist/text/TextShader.d.ts +15 -0
- package/dist/text/TextShader.d.ts.map +1 -0
- package/dist/text/mod.d.ts +4 -0
- package/dist/text/mod.d.ts.map +1 -0
- package/dist/text/shaping.d.ts +39 -0
- package/dist/text/shaping.d.ts.map +1 -0
- package/dist/text/text.wgsl.d.ts +3 -0
- package/dist/text/text.wgsl.d.ts.map +1 -0
- package/dist/textures/AssetManager.d.ts +182 -0
- package/dist/textures/AssetManager.d.ts.map +1 -0
- package/dist/textures/TextureComputeShader.d.ts +21 -0
- package/dist/textures/TextureComputeShader.d.ts.map +1 -0
- package/dist/textures/mod.d.ts +2 -0
- package/dist/textures/mod.d.ts.map +1 -0
- package/dist/textures/pixel-scraping.wgsl.d.ts +3 -0
- package/dist/textures/pixel-scraping.wgsl.d.ts.map +1 -0
- package/dist/textures/types.d.ts +177 -0
- package/dist/textures/types.d.ts.map +1 -0
- package/dist/textures/util.d.ts +8 -0
- package/dist/textures/util.d.ts.map +1 -0
- package/dist/utils/assert.d.ts +2 -0
- package/dist/utils/assert.d.ts.map +1 -0
- package/dist/utils/boilerplate.d.ts +11 -0
- package/dist/utils/boilerplate.d.ts.map +1 -0
- package/dist/utils/error.d.ts +8 -0
- package/dist/utils/error.d.ts.map +1 -0
- package/dist/utils/mod.d.ts +3 -0
- package/dist/utils/mod.d.ts.map +1 -0
- package/dist/utils/pool.d.ts +23 -0
- package/dist/utils/pool.d.ts.map +1 -0
- package/package.json +47 -0
- package/src/Toodle.ts +853 -0
- package/src/colors/mod.ts +151 -0
- package/src/coreTypes/Color.ts +1 -0
- package/src/coreTypes/Point.ts +7 -0
- package/src/coreTypes/Size.ts +4 -0
- package/src/coreTypes/Transform.ts +16 -0
- package/src/coreTypes/Vec2.ts +7 -0
- package/src/coreTypes/mod.ts +5 -0
- package/src/globals.d.ts +4 -0
- package/src/limits.ts +23 -0
- package/src/math/angle.ts +17 -0
- package/src/math/matrix.ts +99 -0
- package/src/math/mod.ts +2 -0
- package/src/mod.ts +22 -0
- package/src/scene/Batcher.ts +61 -0
- package/src/scene/Camera.ts +69 -0
- package/src/scene/JumboQuadNode.ts +219 -0
- package/src/scene/QuadNode.ts +403 -0
- package/src/scene/RenderComponent.ts +12 -0
- package/src/scene/SceneNode.ts +668 -0
- package/src/scene/mod.ts +4 -0
- package/src/screen/mod.ts +1 -0
- package/src/screen/resolution.ts +1 -0
- package/src/shaders/EngineUniform.ts +11 -0
- package/src/shaders/IShader.ts +20 -0
- package/src/shaders/QuadShader.ts +288 -0
- package/src/shaders/ShaderDescriptor.ts +6 -0
- package/src/shaders/mod.ts +5 -0
- package/src/shaders/parser.ts +221 -0
- package/src/shaders/postprocess/blur.ts +245 -0
- package/src/shaders/postprocess/mod.ts +71 -0
- package/src/shaders/samplers.ts +13 -0
- package/src/shaders/wgsl/example.wgsl.ts +24 -0
- package/src/shaders/wgsl/hello.wgsl.ts +62 -0
- package/src/shaders/wgsl/helloInstanced.wgsl.ts +46 -0
- package/src/shaders/wgsl/quad.wgsl.ts +140 -0
- package/src/text/FontPipeline.ts +212 -0
- package/src/text/MsdfFont.ts +190 -0
- package/src/text/TextFormatting.ts +28 -0
- package/src/text/TextNode.ts +82 -0
- package/src/text/TextShader.ts +223 -0
- package/src/text/mod.ts +8 -0
- package/src/text/shaping.ts +280 -0
- package/src/text/text.wgsl.ts +149 -0
- package/src/textures/AssetManager.ts +746 -0
- package/src/textures/TextureComputeShader.ts +434 -0
- package/src/textures/mod.ts +1 -0
- package/src/textures/pixel-scraping.wgsl.ts +131 -0
- package/src/textures/types.ts +182 -0
- package/src/textures/util.ts +352 -0
- package/src/utils/assert.ts +5 -0
- package/src/utils/boilerplate.ts +110 -0
- package/src/utils/error.ts +14 -0
- package/src/utils/mod.ts +2 -0
- package/src/utils/pool.ts +42 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import computeShader from "./pixel-scraping.wgsl";
|
|
2
|
+
import type { TextureWithMetadata } from "./types";
|
|
3
|
+
|
|
4
|
+
// Constants
|
|
5
|
+
const BOUNDING_BOX_SIZE = 4 * Uint32Array.BYTES_PER_ELEMENT;
|
|
6
|
+
const WORKGROUP_SIZE = 8;
|
|
7
|
+
const MAX_BOUND = 0xffffffff;
|
|
8
|
+
const MIN_BOUND = 0x00000000;
|
|
9
|
+
const BYTES_PER_PIXEL = 4;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The data returned by the compute shader that represents the opaque pixels in a texture.
|
|
13
|
+
* Texel coordinates start at 0,0 in the top-left corner of the texture.
|
|
14
|
+
*/
|
|
15
|
+
type OpaqueRect = {
|
|
16
|
+
/** The leftmost texel coordinate of the bounding box. */
|
|
17
|
+
texelX: number;
|
|
18
|
+
/** The topmost texel coordinate of the bounding box. */
|
|
19
|
+
texelY: number;
|
|
20
|
+
/** The width of the bounding box in texels. */
|
|
21
|
+
texelWidth: number;
|
|
22
|
+
/** The height of the bounding box in texels. */
|
|
23
|
+
texelHeight: number;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A GPU-based texture processor that uses compute shaders to:
|
|
28
|
+
* 1. Find the non-transparent bounding box in a texture.
|
|
29
|
+
* 2. Crop the texture to that bounding box.
|
|
30
|
+
* 3. Create a fallback texture if no non-transparent pixels are found.
|
|
31
|
+
*/
|
|
32
|
+
export class TextureComputeShader {
|
|
33
|
+
#device: GPUDevice;
|
|
34
|
+
#boundingBuffer: GPUBuffer;
|
|
35
|
+
#cropPipeline: GPUComputePipeline;
|
|
36
|
+
#boundPipeline: GPUComputePipeline;
|
|
37
|
+
#missingTexturePipeline: GPUComputePipeline;
|
|
38
|
+
|
|
39
|
+
constructor(
|
|
40
|
+
device: GPUDevice,
|
|
41
|
+
cropPipeline: GPUComputePipeline,
|
|
42
|
+
boundPipeline: GPUComputePipeline,
|
|
43
|
+
missingTexturePipeline: GPUComputePipeline,
|
|
44
|
+
) {
|
|
45
|
+
this.#device = device;
|
|
46
|
+
this.#boundPipeline = boundPipeline;
|
|
47
|
+
this.#cropPipeline = cropPipeline;
|
|
48
|
+
this.#missingTexturePipeline = missingTexturePipeline;
|
|
49
|
+
|
|
50
|
+
// Buffer to store the computed bounding box [minX, minY, maxX, maxY]
|
|
51
|
+
this.#boundingBuffer = this.#device.createBuffer({
|
|
52
|
+
size: BOUNDING_BOX_SIZE,
|
|
53
|
+
usage:
|
|
54
|
+
GPUBufferUsage.STORAGE |
|
|
55
|
+
GPUBufferUsage.COPY_SRC |
|
|
56
|
+
GPUBufferUsage.COPY_DST,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Factory method to initialize pipelines and return an instance of TextureComputeShader.
|
|
62
|
+
*/
|
|
63
|
+
static create(device: GPUDevice) {
|
|
64
|
+
const pipelines = createPipelines(device, "TextureComputeShader");
|
|
65
|
+
return new TextureComputeShader(
|
|
66
|
+
device,
|
|
67
|
+
pipelines.cropPipeline,
|
|
68
|
+
pipelines.boundPipeline,
|
|
69
|
+
pipelines.missingTexturePipeline,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Main entry point to process a texture.
|
|
75
|
+
* Returns a cropped ImageBitmap and metadata.
|
|
76
|
+
*/
|
|
77
|
+
async processTexture(
|
|
78
|
+
textureWrapper: TextureWithMetadata,
|
|
79
|
+
): Promise<TextureWithMetadata> {
|
|
80
|
+
const boundsBindGroup = this.#boundsBindGroup(textureWrapper.texture);
|
|
81
|
+
|
|
82
|
+
const commandEncoder = this.#device.createCommandEncoder();
|
|
83
|
+
const passEncoder = commandEncoder.beginComputePass();
|
|
84
|
+
|
|
85
|
+
const dispatchX = Math.ceil(textureWrapper.texture.width / WORKGROUP_SIZE);
|
|
86
|
+
const dispatchY = Math.ceil(textureWrapper.texture.height / WORKGROUP_SIZE);
|
|
87
|
+
|
|
88
|
+
// Initialize bounding box with max/min values
|
|
89
|
+
const boundsInit = new Uint32Array([
|
|
90
|
+
MAX_BOUND,
|
|
91
|
+
MAX_BOUND,
|
|
92
|
+
MIN_BOUND,
|
|
93
|
+
MIN_BOUND,
|
|
94
|
+
]);
|
|
95
|
+
this.#device.queue.writeBuffer(
|
|
96
|
+
this.#boundingBuffer,
|
|
97
|
+
0,
|
|
98
|
+
boundsInit.buffer,
|
|
99
|
+
0,
|
|
100
|
+
BOUNDING_BOX_SIZE,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
// Run bounds detection compute shader
|
|
104
|
+
passEncoder.setPipeline(this.#boundPipeline);
|
|
105
|
+
passEncoder.setBindGroup(0, boundsBindGroup);
|
|
106
|
+
passEncoder.dispatchWorkgroups(dispatchX, dispatchY);
|
|
107
|
+
passEncoder.end();
|
|
108
|
+
this.#device.queue.submit([commandEncoder.finish()]);
|
|
109
|
+
|
|
110
|
+
const { texelX, texelY, texelWidth, texelHeight, computeBuffer } =
|
|
111
|
+
await this.#getBoundingBox();
|
|
112
|
+
|
|
113
|
+
// If no non-transparent pixels were found
|
|
114
|
+
if (texelX === MAX_BOUND || texelY === MAX_BOUND) {
|
|
115
|
+
return await this.#createMissingTexture(textureWrapper.texture);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Crop the texture to the computed bounds
|
|
119
|
+
const croppedTexture = await this.#cropTexture(
|
|
120
|
+
texelWidth,
|
|
121
|
+
texelHeight,
|
|
122
|
+
computeBuffer,
|
|
123
|
+
textureWrapper.texture,
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const leftCrop = texelX;
|
|
127
|
+
const rightCrop = textureWrapper.originalSize.width - texelX - texelWidth;
|
|
128
|
+
const topCrop = texelY;
|
|
129
|
+
const bottomCrop =
|
|
130
|
+
textureWrapper.originalSize.height - texelY - texelHeight;
|
|
131
|
+
|
|
132
|
+
textureWrapper = {
|
|
133
|
+
texture: croppedTexture,
|
|
134
|
+
cropOffset: { x: leftCrop - rightCrop, y: bottomCrop - topCrop },
|
|
135
|
+
originalSize: textureWrapper.originalSize,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
return textureWrapper;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Reads the GPU buffer containing the bounding box.
|
|
143
|
+
*/
|
|
144
|
+
async #getBoundingBox(): Promise<
|
|
145
|
+
OpaqueRect & { computeBuffer: Uint32Array }
|
|
146
|
+
> {
|
|
147
|
+
const readBuffer = this.#device.createBuffer({
|
|
148
|
+
label: "AABB Compute Buffer",
|
|
149
|
+
size: BOUNDING_BOX_SIZE,
|
|
150
|
+
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const copyEncoder = this.#device.createCommandEncoder();
|
|
154
|
+
copyEncoder.copyBufferToBuffer(
|
|
155
|
+
this.#boundingBuffer,
|
|
156
|
+
0,
|
|
157
|
+
readBuffer,
|
|
158
|
+
0,
|
|
159
|
+
BOUNDING_BOX_SIZE,
|
|
160
|
+
);
|
|
161
|
+
this.#device.queue.submit([copyEncoder.finish()]);
|
|
162
|
+
|
|
163
|
+
await readBuffer.mapAsync(GPUMapMode.READ);
|
|
164
|
+
const computeBuffer = new Uint32Array(readBuffer.getMappedRange().slice(0));
|
|
165
|
+
readBuffer.unmap();
|
|
166
|
+
|
|
167
|
+
const [minX, minY, maxX, maxY] = computeBuffer;
|
|
168
|
+
return {
|
|
169
|
+
texelX: minX,
|
|
170
|
+
texelY: minY,
|
|
171
|
+
texelWidth: maxX - minX + 1,
|
|
172
|
+
texelHeight: maxY - minY + 1,
|
|
173
|
+
|
|
174
|
+
computeBuffer,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Crops the original texture to the specified bounds using a compute shader.
|
|
180
|
+
*/
|
|
181
|
+
async #cropTexture(
|
|
182
|
+
croppedWidth: number,
|
|
183
|
+
croppedHeight: number,
|
|
184
|
+
computeBuffer: Uint32Array,
|
|
185
|
+
inputTexture: GPUTexture,
|
|
186
|
+
) {
|
|
187
|
+
const boundsUniform = this.#device.createBuffer({
|
|
188
|
+
label: "Cropping Bounds Uniform Buffer",
|
|
189
|
+
size: BOUNDING_BOX_SIZE,
|
|
190
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
191
|
+
});
|
|
192
|
+
this.#device.queue.writeBuffer(boundsUniform, 0, computeBuffer);
|
|
193
|
+
|
|
194
|
+
const outputTexture = this.#device.createTexture({
|
|
195
|
+
label: "Cropped Texture",
|
|
196
|
+
size: [croppedWidth, croppedHeight],
|
|
197
|
+
format: "rgba8unorm",
|
|
198
|
+
usage:
|
|
199
|
+
GPUTextureUsage.STORAGE_BINDING |
|
|
200
|
+
GPUTextureUsage.TEXTURE_BINDING |
|
|
201
|
+
GPUTextureUsage.COPY_SRC,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const dimensionsOutBuffer = this.#device.createBuffer({
|
|
205
|
+
label: "Cropping Dimensions Output Buffer",
|
|
206
|
+
size: BOUNDING_BOX_SIZE,
|
|
207
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const bindGroup = this.#croppingBindGroup(
|
|
211
|
+
inputTexture,
|
|
212
|
+
outputTexture,
|
|
213
|
+
boundsUniform,
|
|
214
|
+
dimensionsOutBuffer,
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
const encoder = this.#device.createCommandEncoder();
|
|
218
|
+
const pass = encoder.beginComputePass();
|
|
219
|
+
pass.setPipeline(this.#cropPipeline);
|
|
220
|
+
pass.setBindGroup(0, bindGroup);
|
|
221
|
+
pass.dispatchWorkgroups(
|
|
222
|
+
Math.ceil(croppedWidth / WORKGROUP_SIZE),
|
|
223
|
+
Math.ceil(croppedHeight / WORKGROUP_SIZE),
|
|
224
|
+
);
|
|
225
|
+
pass.end();
|
|
226
|
+
this.#device.queue.submit([encoder.finish()]);
|
|
227
|
+
|
|
228
|
+
return outputTexture;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Creates a fallback placeholder texture if the input is fully transparent.
|
|
233
|
+
*/
|
|
234
|
+
async #createMissingTexture(
|
|
235
|
+
inputTexture: GPUTexture,
|
|
236
|
+
): Promise<TextureWithMetadata> {
|
|
237
|
+
const placeholder = this.#device.createTexture({
|
|
238
|
+
label: "Missing Placeholder Texture",
|
|
239
|
+
size: [inputTexture.width, inputTexture.height],
|
|
240
|
+
format: "rgba8unorm",
|
|
241
|
+
usage:
|
|
242
|
+
GPUTextureUsage.COPY_SRC |
|
|
243
|
+
GPUTextureUsage.COPY_DST |
|
|
244
|
+
GPUTextureUsage.STORAGE_BINDING |
|
|
245
|
+
GPUTextureUsage.TEXTURE_BINDING,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const encoder = this.#device.createCommandEncoder();
|
|
249
|
+
const pass = encoder.beginComputePass();
|
|
250
|
+
pass.setPipeline(this.#missingTexturePipeline);
|
|
251
|
+
pass.setBindGroup(0, this.#missingTextureBindGroup(placeholder));
|
|
252
|
+
pass.dispatchWorkgroups(placeholder.width / 8, placeholder.height / 8);
|
|
253
|
+
pass.end();
|
|
254
|
+
this.#device.queue.submit([encoder.finish()]);
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
texture: placeholder,
|
|
258
|
+
cropOffset: { x: 0, y: 0 },
|
|
259
|
+
originalSize: { width: inputTexture.width, height: inputTexture.height },
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Converts a GPUTexture to an ImageBitmap for display or further use.
|
|
265
|
+
*/
|
|
266
|
+
async #textureToBitmap(
|
|
267
|
+
texture: GPUTexture,
|
|
268
|
+
width: number,
|
|
269
|
+
height: number,
|
|
270
|
+
): Promise<ImageBitmap> {
|
|
271
|
+
const paddedBytesPerRow = Math.ceil((width * BYTES_PER_PIXEL) / 256) * 256;
|
|
272
|
+
const bufferSize = paddedBytesPerRow * height;
|
|
273
|
+
|
|
274
|
+
const readBuffer = this.#device.createBuffer({
|
|
275
|
+
size: bufferSize,
|
|
276
|
+
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
const encoder = this.#device.createCommandEncoder();
|
|
280
|
+
encoder.copyTextureToBuffer(
|
|
281
|
+
{ texture },
|
|
282
|
+
{ buffer: readBuffer, bytesPerRow: paddedBytesPerRow },
|
|
283
|
+
{ width, height, depthOrArrayLayers: 1 },
|
|
284
|
+
);
|
|
285
|
+
this.#device.queue.submit([encoder.finish()]);
|
|
286
|
+
|
|
287
|
+
await readBuffer.mapAsync(GPUMapMode.READ);
|
|
288
|
+
const raw = readBuffer.getMappedRange();
|
|
289
|
+
const rawArray = new Uint8Array(raw);
|
|
290
|
+
|
|
291
|
+
const pixelData = new Uint8ClampedArray(width * height * 4);
|
|
292
|
+
for (let y = 0; y < height; y++) {
|
|
293
|
+
const src = y * paddedBytesPerRow;
|
|
294
|
+
const dst = y * width * 4;
|
|
295
|
+
pixelData.set(rawArray.subarray(src, src + width * 4), dst);
|
|
296
|
+
}
|
|
297
|
+
readBuffer.unmap();
|
|
298
|
+
|
|
299
|
+
const canvas = document.createElement("canvas");
|
|
300
|
+
canvas.width = width;
|
|
301
|
+
canvas.height = height;
|
|
302
|
+
const ctx = canvas.getContext("2d")!;
|
|
303
|
+
ctx.putImageData(new ImageData(pixelData, width, height), 0, 0);
|
|
304
|
+
|
|
305
|
+
return await createImageBitmap(canvas);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Bind group helpers
|
|
309
|
+
|
|
310
|
+
#boundsBindGroup(inputTexture: GPUTexture): GPUBindGroup {
|
|
311
|
+
return this.#device.createBindGroup({
|
|
312
|
+
layout: this.#boundPipeline.getBindGroupLayout(0),
|
|
313
|
+
entries: [
|
|
314
|
+
{ binding: 0, resource: inputTexture.createView() },
|
|
315
|
+
{ binding: 1, resource: { buffer: this.#boundingBuffer } },
|
|
316
|
+
],
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
#croppingBindGroup(
|
|
321
|
+
inputTexture: GPUTexture,
|
|
322
|
+
outputTexture: GPUTexture,
|
|
323
|
+
boundsUniform: GPUBuffer,
|
|
324
|
+
dimensionsOutBuffer: GPUBuffer,
|
|
325
|
+
): GPUBindGroup {
|
|
326
|
+
return this.#device.createBindGroup({
|
|
327
|
+
layout: this.#cropPipeline.getBindGroupLayout(0),
|
|
328
|
+
entries: [
|
|
329
|
+
{ binding: 0, resource: inputTexture.createView() },
|
|
330
|
+
{ binding: 1, resource: outputTexture.createView() },
|
|
331
|
+
{ binding: 2, resource: { buffer: boundsUniform } },
|
|
332
|
+
{ binding: 3, resource: { buffer: dimensionsOutBuffer } },
|
|
333
|
+
],
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
#missingTextureBindGroup(outputTexture: GPUTexture): GPUBindGroup {
|
|
338
|
+
return this.#device.createBindGroup({
|
|
339
|
+
layout: this.#missingTexturePipeline.getBindGroupLayout(0),
|
|
340
|
+
entries: [{ binding: 0, resource: outputTexture.createView() }],
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Creates compute pipelines for bounding box detection, cropping, and fallback texture generation.
|
|
347
|
+
*/
|
|
348
|
+
function createPipelines(device: GPUDevice, label: string) {
|
|
349
|
+
const shader = device.createShaderModule({
|
|
350
|
+
label: `${label} Shader`,
|
|
351
|
+
code: computeShader,
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
const findBoundsBindGroupLayout = device.createBindGroupLayout({
|
|
355
|
+
label: "Bounds Detection Layout",
|
|
356
|
+
entries: [
|
|
357
|
+
{
|
|
358
|
+
binding: 0,
|
|
359
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
360
|
+
texture: { sampleType: "float", viewDimension: "2d" },
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
binding: 1,
|
|
364
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
365
|
+
buffer: { type: "storage" },
|
|
366
|
+
},
|
|
367
|
+
],
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
const cropBindGroupLayout = device.createBindGroupLayout({
|
|
371
|
+
label: "Cropping Layout",
|
|
372
|
+
entries: [
|
|
373
|
+
{ binding: 0, visibility: GPUShaderStage.COMPUTE, texture: {} },
|
|
374
|
+
{
|
|
375
|
+
binding: 1,
|
|
376
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
377
|
+
storageTexture: {
|
|
378
|
+
access: "write-only",
|
|
379
|
+
format: "rgba8unorm",
|
|
380
|
+
viewDimension: "2d",
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
binding: 2,
|
|
385
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
386
|
+
buffer: { type: "uniform" },
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
binding: 3,
|
|
390
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
391
|
+
buffer: { type: "storage" },
|
|
392
|
+
},
|
|
393
|
+
],
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
const missingTextureBindGroupLayout = device.createBindGroupLayout({
|
|
397
|
+
label: "Missing Texture Layout",
|
|
398
|
+
entries: [
|
|
399
|
+
{
|
|
400
|
+
binding: 0,
|
|
401
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
402
|
+
storageTexture: {
|
|
403
|
+
access: "write-only",
|
|
404
|
+
format: "rgba8unorm",
|
|
405
|
+
viewDimension: "2d",
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
],
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
return {
|
|
412
|
+
boundPipeline: device.createComputePipeline({
|
|
413
|
+
label: `${label} - Find Bounds Pipeline`,
|
|
414
|
+
layout: device.createPipelineLayout({
|
|
415
|
+
bindGroupLayouts: [findBoundsBindGroupLayout],
|
|
416
|
+
}),
|
|
417
|
+
compute: { module: shader, entryPoint: "find_bounds" },
|
|
418
|
+
}),
|
|
419
|
+
cropPipeline: device.createComputePipeline({
|
|
420
|
+
label: `${label} - Crop Pipeline`,
|
|
421
|
+
layout: device.createPipelineLayout({
|
|
422
|
+
bindGroupLayouts: [cropBindGroupLayout],
|
|
423
|
+
}),
|
|
424
|
+
compute: { module: shader, entryPoint: "crop_and_output" },
|
|
425
|
+
}),
|
|
426
|
+
missingTexturePipeline: device.createComputePipeline({
|
|
427
|
+
label: `${label} - Missing Texture Pipeline`,
|
|
428
|
+
layout: device.createPipelineLayout({
|
|
429
|
+
bindGroupLayouts: [missingTextureBindGroupLayout],
|
|
430
|
+
}),
|
|
431
|
+
compute: { module: shader, entryPoint: "missing_texture" },
|
|
432
|
+
}),
|
|
433
|
+
};
|
|
434
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type * from "./types";
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
export default /*wgsl*/ `
|
|
2
|
+
// ==============================
|
|
3
|
+
// === BOUNDING BOX PASS =======
|
|
4
|
+
// ==============================
|
|
5
|
+
|
|
6
|
+
// Input texture from which to compute the non-transparent bounding box
|
|
7
|
+
@group(0) @binding(0)
|
|
8
|
+
var input_texture: texture_2d<f32>;
|
|
9
|
+
|
|
10
|
+
// Atomic bounding box storage structure
|
|
11
|
+
struct bounding_box_atomic {
|
|
12
|
+
min_x: atomic<u32>,
|
|
13
|
+
min_y: atomic<u32>,
|
|
14
|
+
max_x: atomic<u32>,
|
|
15
|
+
max_y: atomic<u32>,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Storage buffer to hold atomic bounding box updates
|
|
19
|
+
@group(0) @binding(1)
|
|
20
|
+
var<storage, read_write> bounds: bounding_box_atomic;
|
|
21
|
+
|
|
22
|
+
// Compute shader to find the bounding box of non-transparent pixels
|
|
23
|
+
@compute @workgroup_size(8, 8)
|
|
24
|
+
fn find_bounds(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
25
|
+
let size = textureDimensions(input_texture).xy;
|
|
26
|
+
if (gid.x >= size.x || gid.y >= size.y) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let pixel = textureLoad(input_texture, vec2<i32>(gid.xy), 0);
|
|
31
|
+
if (pixel.a > 0.0) {
|
|
32
|
+
atomicMin(&bounds.min_x, gid.x);
|
|
33
|
+
atomicMin(&bounds.min_y, gid.y);
|
|
34
|
+
atomicMax(&bounds.max_x, gid.x);
|
|
35
|
+
atomicMax(&bounds.max_y, gid.y);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ==============================
|
|
40
|
+
// === CROP + OUTPUT PASS ======
|
|
41
|
+
// ==============================
|
|
42
|
+
|
|
43
|
+
// Input texture from which cropped data is read
|
|
44
|
+
@group(0) @binding(0)
|
|
45
|
+
var input_texture_crop: texture_2d<f32>;
|
|
46
|
+
|
|
47
|
+
// Output texture where cropped image is written
|
|
48
|
+
@group(0) @binding(1)
|
|
49
|
+
var output_texture: texture_storage_2d<rgba8unorm, write>;
|
|
50
|
+
|
|
51
|
+
// Bounding box passed in as a uniform (not atomic anymore)
|
|
52
|
+
struct bounding_box_uniform {
|
|
53
|
+
min_x: u32,
|
|
54
|
+
min_y: u32,
|
|
55
|
+
max_x: u32,
|
|
56
|
+
max_y: u32,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
@group(0) @binding(2)
|
|
60
|
+
var<uniform> bounds_uniform: bounding_box_uniform;
|
|
61
|
+
|
|
62
|
+
// Struct to store both original and cropped texture dimensions
|
|
63
|
+
struct image_dimensions {
|
|
64
|
+
original_width: u32,
|
|
65
|
+
original_height: u32,
|
|
66
|
+
cropped_width: u32,
|
|
67
|
+
cropped_height: u32,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Storage buffer to output the result dimensions
|
|
71
|
+
@group(0) @binding(3)
|
|
72
|
+
var<storage, read_write> dimensions_out: image_dimensions;
|
|
73
|
+
|
|
74
|
+
// Compute shader to crop the input texture to the bounding box and output it
|
|
75
|
+
@compute @workgroup_size(8, 8)
|
|
76
|
+
fn crop_and_output(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
77
|
+
let size = textureDimensions(input_texture_crop).xy;
|
|
78
|
+
|
|
79
|
+
let crop_width = bounds_uniform.max_x - bounds_uniform.min_x + 1u;
|
|
80
|
+
let crop_height = bounds_uniform.max_y - bounds_uniform.min_y + 1u;
|
|
81
|
+
|
|
82
|
+
if (gid.x >= crop_width || gid.y >= crop_height) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let src_coord = vec2<i32>(
|
|
87
|
+
i32(bounds_uniform.min_x + gid.x),
|
|
88
|
+
i32(bounds_uniform.min_y + gid.y)
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
let dst_coord = vec2<i32>(i32(gid.x), i32(gid.y));
|
|
92
|
+
let pixel = textureLoad(input_texture_crop, src_coord, 0);
|
|
93
|
+
textureStore(output_texture, dst_coord, pixel);
|
|
94
|
+
|
|
95
|
+
// Output dimensions from workgroup (0,0) only
|
|
96
|
+
if (gid.x == 0u && gid.y == 0u) {
|
|
97
|
+
dimensions_out.original_width = size.x;
|
|
98
|
+
dimensions_out.original_height = size.y;
|
|
99
|
+
dimensions_out.cropped_width = crop_width;
|
|
100
|
+
dimensions_out.cropped_height = crop_height;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ==============================
|
|
105
|
+
// === MISSING TEXTURE FILL ====
|
|
106
|
+
// ==============================
|
|
107
|
+
|
|
108
|
+
// Output texture to draw a fallback checkerboard
|
|
109
|
+
@group(0) @binding(0)
|
|
110
|
+
var checker_texture: texture_storage_2d<rgba8unorm, write>;
|
|
111
|
+
|
|
112
|
+
// Compute shader to fill a texture with a purple & green checkerboard
|
|
113
|
+
@compute @workgroup_size(8, 8)
|
|
114
|
+
fn missing_texture(@builtin(global_invocation_id) id: vec3<u32>) {
|
|
115
|
+
let size = textureDimensions(checker_texture);
|
|
116
|
+
if (id.x >= size.x || id.y >= size.y) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let checker_size = 25u;
|
|
121
|
+
let on_color = ((id.x / checker_size + id.y / checker_size) % 2u) == 0u;
|
|
122
|
+
|
|
123
|
+
let color = select(
|
|
124
|
+
vec4<f32>(0.5, 0.0, 0.5, 1.0), // Purple
|
|
125
|
+
vec4<f32>(0.0, 1.0, 0.0, 1.0), // Green
|
|
126
|
+
on_color
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
textureStore(checker_texture, vec2<i32>(id.xy), color);
|
|
130
|
+
}
|
|
131
|
+
`;
|