Skip to content

@typegpu/kitmipmaps

Mipmaps

Fill a texture's mip levels in your encoder, each texel combining a 2×2 block of the level above.

kit.mipmaps

module

mipmaps.create(root, texture, { combine, … }) → Mipmaps

rootborrows
the root
texturetakes
a float texture with storage usage, or render usage, and sampled usage
Mipmapsreturns
mipmaps the garbage collector frees with the texture
GPU functions
meanlargestsmallest
types
CombineMipmaps
Optionscreate · 2 fields
combine

Combine

Combines a 2×2 block of the level above into one texel, as a 'use gpu' function.

source?

TgpuTexture & SampledFlag= leaving level 0 to you

Fills level 0 from this texture's 2×2 blocks too, such as the depth a pyramid of farthest depths starts from.

records
  • recordcompute
  • of().recordcompute

record after whatever writes level 0 or source, before what reads the levels.

owns

a pipeline ·a bind group per level

The kernel or the draw that fills a level, and the views each level reads and writes.

lifecycle

initAsync()destroy()(none)[Symbol.dispose](none)

used by

Ocean

Tap a texel, or use the arrow keys, to light the texels it combines in every level before it. Level 0 holds depths: a near box on a far wall. With max, each texel keeps the farthest depth under it, so a box nearer than that is in front of everything there. With mean, each texel averages its block, as sampling a smaller level wants.

A texture you draw into, such as an atlas or a depth buffer, has only its first level. Sampling it small needs the smaller levels averaged from it, and an occlusion test needs each level to keep the farthest depth under it, so one texel answers for a whole block of the screen. mipmaps.create fills the levels from your 'use gpu' function of each 2×2 block, in the encoder or compute pass of your frame.

import {
import mipmaps
mipmaps
} from '@typegpu/kit';
const
const depth: TgpuTexture<{
size: [1280, 720];
format: "depth32float";
}> & RenderFlag & SampledFlag
depth
=
const root: TgpuRoot
root
.
TgpuRoot.createTexture<number, number, number, [1280, 720], "depth32float", number, number, GPUTextureFormat[], GPUTextureDimension>(props: CreateTextureOptions<[1280, 720], "depth32float", number, number, GPUTextureFormat[], GPUTextureDimension>): TgpuTexture<{
size: [1280, 720];
format: "depth32float";
}>
createTexture
({
size: [1280, 720]

The width, height, and depth or layer count of the texture.

size
: [1280, 720],
format: "depth32float"

The format of the texture.

format
: 'depth32float' })
.
TgpuTexture<{ size: [1280, 720]; format: "depth32float"; }>.$usage<["render", "sampled"]>(usages_0: "render", usages_1: "sampled"): TgpuTexture<{
size: [1280, 720];
format: "depth32float";
}> & RenderFlag & SampledFlag
$usage
('render', 'sampled');
// Half the depth's size, rounded up to powers of two, so every level halves evenly. A screen
// coordinate `uv` reads it at `uv * [640, 360] / [1024, 512]`
const
const farthest: TgpuTexture<{
size: [1024, 512];
format: "r32float";
mipLevelCount: 11;
}> & SampledFlag & StorageFlag
farthest
=
const root: TgpuRoot
root
.
TgpuRoot.createTexture<number, number, number, [1024, 512], "r32float", 11, number, GPUTextureFormat[], GPUTextureDimension>(props: CreateTextureOptions<[1024, 512], "r32float", 11, number, GPUTextureFormat[], GPUTextureDimension>): TgpuTexture<{
size: [1024, 512];
format: "r32float";
mipLevelCount: 11;
}>
createTexture
({
size: [1024, 512]

The width, height, and depth or layer count of the texture.

size
: [1024, 512],
format: "r32float"

The format of the texture.

format
: 'r32float',
mipLevelCount?: 11 | undefined

The number of mip levels the texture will contain.

@default ― 1

mipLevelCount
: 11 })
.
TgpuTexture<{ size: [1024, 512]; format: "r32float"; mipLevelCount: 11; }>.$usage<["storage", "sampled"]>(usages_0: "storage", usages_1: "sampled"): TgpuTexture<{
size: [1024, 512];
format: "r32float";
mipLevelCount: 11;
}> & SampledFlag & StorageFlag
$usage
('storage', 'sampled');
const
const pyramid: mipmaps.Mipmaps
pyramid
=
import mipmaps
mipmaps
.
function create(root: TgpuRoot, texture: TgpuTexture & SampledFlag, options: mipmaps.Options): mipmaps.Mipmaps

Creates what fills a texture's mip levels, every array layer of each, from the level above. A level of odd size leaves the last texels of the one above out.

@owns ― a pipeline, a bind group per level: the kernel or the draw that fills a level, and the views each level reads and writes

@records ― record after whatever writes level 0 or source, before what reads the levels

@param ― texture a float texture with storage usage, or render usage, and sampled usage

@returns ― mipmaps the garbage collector frees with the texture

create
(
const root: TgpuRoot
root
,
const farthest: TgpuTexture<{
size: [1024, 512];
format: "r32float";
mipLevelCount: 11;
}> & SampledFlag & StorageFlag
farthest
, {
Options.combine: mipmaps.Combine

Combines a 2×2 block of the level above into one texel, as a 'use gpu' function.

combine
:
import mipmaps
mipmaps
.
const largest: mipmaps.Combine

Keeps the largest of the four per channel, such as the farthest depth over a block.

largest
,
Options.source?: (TgpuTexture<any> & SampledFlag) | undefined

Fills level 0 from this texture's 2×2 blocks too, such as the depth a pyramid of farthest depths starts from. It has as many array layers, and blocks past its edge repeat its last texels. Default leaving level 0 to you.

source
:
const depth: TgpuTexture<{
size: [1280, 720];
format: "depth32float";
}> & RenderFlag & SampledFlag
depth
});
import frames
frames
.
function create(root: TgpuRoot, record: Recorder): frames.Frames

Records a frame into one encoder and submits once, with a clock. Borrows the root. The synchronous recorder borrows each frame's encoder.

@submits ― always once a frame after your recorder returns, and again at each frame.submit()

@param ― record your frame, recorded into each frame's encoder

@returns ― a loop you run with each frame's time and destroy

create
(
const root: TgpuRoot
root
, (
frame: frames.Frame
frame
) => {
// ... the depth prepass
const
const compute: TgpuComputePass
compute
=
frame: frames.Frame
frame
.
Frame.encoder: TgpuCommandEncoder

Records commands for submission after the recorder returns. submit() replaces this encoder. Read it at each call instead of keeping it, and let the frame submit it.

encoder
.
TgpuCommandEncoder.beginComputePass(descriptor?: TgpuComputePassDescriptor): TgpuComputePass

Begins recording a compute pass

beginComputePass
();
const pyramid: mipmaps.Mipmaps
pyramid
.
Mipmaps.record(target: TgpuCommandEncoder | TgpuComputePass): void

Records the levels in order. A texture with storage usage fills in a compute pass, the pass given or one of its own on the encoder. Another fills in render passes on the encoder.

record
(
const compute: TgpuComputePass
compute
);
// ... the kernel that culls what the depth hides
const compute: TgpuComputePass
compute
.
TgpuComputePass.end(): void

Completes the recording of this compute pass

end
();
});

combine takes the block’s four texels as vec4f, left to right and then top to bottom, and returns the texel below them. The module exports three:

  • mipmaps.mean averages them, the filter for textures you sample.
  • mipmaps.largest and mipmaps.smallest keep the largest or smallest per channel, such as the farthest depth with depth that grows away from the eye.

A function of your own can weigh the texels by their alpha, so empty texels around a cutout leave its colour alone.

Level 0 is yours to draw or write, and record fills level 1 and on from it. With source, record fills level 0 too, from that texture’s blocks. A source larger than two texels per texel of level 0 leaves its far part out; one smaller repeats its last row and column.

Levels fill as a baker’s texture does: one dispatch per level for a texture with storage usage, and otherwise a render pass per level and array layer, such as for the rgba8unorm-srgb atlas of impostors. Both fill every array layer of a level.

create makes a bind group per level of its texture. of(texture, source) fills another texture of the same format and usage through the same pipeline, with bind groups of its own, such as the other texture of a pair. For a target that a resize replaces, call of through memo with the current texture, so a resize makes bind groups and no pipeline.

  • The texture holds floats: a float or unfilterable-float sample type.
  • A level of odd size leaves the last row or column of the level above out. Size the texture in powers of two for blocks that cover every texel.