Repository navigation
Releases: with-developer/emberwave
Release list
emberwave v0.11.0
Decoding into the Metal client's own textures, such as the texture of a CVPixelBuffer for an AVSampleBufferDisplayLayer, a C API whose documentation sets out how decodes and the client's reads are ordered, and an iPhone and iPad benchmark app that times and checks every way into textures.
Added
- Decoding into the Metal client's own textures (decision D44, #63).
emberwave_texture_from_metalandemberwave_texture_from_metal_planewrap anid<MTLTexture>of the client's, such as the BGRA texture of aCVPixelBufferfor anAVSampleBufferDisplayLayer. Every decode into textures then fills it.- A copy fills a texture that is not framebuffer-only, as a
CVMetalTextureCache's is not, at a width of a multiple of 64 pixels. A draw fills a render target. - The decodes that do not wait take only textures whose hazards Metal tracks.
- In Rust, a picture's target needs
RENDER_ATTACHMENTonly where a draw fills it. - The plane decodes refuse one Metal texture wrapped for two planes.
- The C API's documentation sets out in one place, under
EmberwaveTexture, how the decodes and the caller's reads of a texture are ordered on each kind of context, and how many textures to keep in turn.
Changed
- The iPhone and iPad benchmark app also times decoding into Y, Cb and Cr textures and on the app's own queue, and checks both through Metal: the planes against those decoded to memory, and a decode on the app's queue against the RGB decode, read by the app's next command buffer without a wait. CI runs the checks in an iOS Simulator.
emberwave v0.10.0
More for Metal clients: the Y, Cb and Cr planes in textures, the fastest way to the GPU; the RGB picture into textures with a copy rather than a draw; and decoding on the client's own queue, which needs no wait and one texture.
Added
- The Y, Cb and Cr planes in textures, for Metal clients that convert the colours themselves (decision D41, #64).
emberwave_texture_create_planemakes a one-channel texture (MTLPixelFormatR8Unorm) of one plane's size, for that plane.emberwave_decoder_decode_plane_texturescopies the planes of the frame into three of them and waits. They hold the stream's samples, byte for byte.- The fastest way to the GPU: on an Apple M3 Max a 4K frame takes 1.0 to 1.1 ms, against 1.3 to 1.4 ms into an RGB texture, and a 1080p frame 0.45 ms against 0.56 ms. In 4:2:0 they take 1.5 bytes a pixel, where RGBA takes 4.
- In Rust,
Decoder::encode_to_plane_textures,decode_assembled_to_plane_texturesandcreate_plane_texture, andPLANE_FORMAT.
- Decoding on the Metal client's own queue, without waiting (decision D43, #62, #63).
emberwave_context_create_metalmakes a context on the client'sid<MTLDevice>andid<MTLCommandQueue>.- On it,
emberwave_decoder_submit_textureandemberwave_decoder_submit_plane_texturesreturn once the work is on the queue. Metal runs the client's command buffers committed afterwards after the decode, and the next decode after them, so one texture takes every frame. - On an Apple M3 Max a 4K frame takes the decoding thread 0.26 ms instead of 1.3 ms, and the client's draw completes as early as after the waiting decode, at 1080p 0.1 ms earlier.
- In Rust,
Decoder::submit_assembled_to_textureandsubmit_assembled_to_plane_textures, andGpuContext::device_descriptorandfrom_devicefor a context on a device made elsewhere.
Changed
- A decoder's packed planes have rows of a multiple of 256 bytes, so that copies take them into textures: 7% more memory for them at 1080p, and as much more to read back to the CPU.
- The RGB picture goes into a texture with a copy, not a draw, when the texture takes copies (
COPY_DST) and the picture's width is a multiple of 64 pixels, as 720p, 1080p, 1440p and 4K are (decision D42, #70). The colour conversion writes blue first for a BGRA texture. On an Apple M3 Max a 4K frame into a texture through the C API takes 1.3 to 1.4 ms instead of 1.5 ms, and a 1080p frame 0.56 ms instead of 0.71 ms. The textures ofemberwave_texture_createandDecoder::create_texturetake copies. The browser player still draws its canvas, as wgpu offers no copies into one.
emberwave v0.9.0
Pictures straight into Metal textures for iPhone, iPad and Mac apps, without a trip through CPU memory, and an iPhone and iPad app that times emberwave on the device's own GPU.
Added
- An iPhone and iPad benchmark app,
crates/emberwave-ffi/examples/ios(#66). It times decoding into Metal textures, decoding to planes in memory and encoding on the device's GPU through the C API, with the pictures and budgets of the browser benchmark. It also draws the last picture from its texture. CI runs it in an iOS Simulator, where it also reads a texture back through Metal. - Pictures on the GPU for Metal clients (decision D40, #27).
emberwave_texture_createmakes a texture of the picture's size, RGBA or BGRA, on the decoder's device.emberwave_decoder_decode_texturedraws the RGB picture into it and waits.emberwave_texture_metalandemberwave_context_metal_devicegive theid<MTLTexture>andid<MTLDevice>behind them.- On an Apple M3 Max a 4K frame takes 1.5 ms that way, against 2.7 ms to RGB in CPU memory.
- In Rust,
Decoder::encode_rgb_to_texture,decode_assembled_to_textureandcreate_texturedraw into a wgpu texture, andGpuContext::idtells contexts apart.
Changed
- The browser player draws its canvas through
Decoder::encode_rgb_to_texture, the same pass as the C API's textures.
emberwave v0.8.0
Faster native clients: a decoded picture reaches the CPU in a third of the time, through the C API and the command line tool, and pictures in textures encode faster. Also fixes from a review of the browser pages, which now hold a file once, survive a lost GPU and refuse files that would ask for gigabytes.
Added
Decoder::decode_assembled_rowsandDecoder::decode_assembled_rgb_rowshand the decoded picture to a closure row by row, straight from the memory the GPU wrote, so that a caller can put it where it wants it with one copy.
Changed
- Encoding from textures is faster (decision D38, #40). The gather pass takes the texels four at a time with
textureGather, where it loaded them one by one. On an Apple M3 Max a 4K frame from textures encodes in 1.71 ms instead of 1.80 ms. - Reading a decoded picture back to the CPU is much faster (decision D37, #56): it comes straight out of the mapped buffer, and RGBA turns into RGB in a loop the compiler vectorises, where a copy per pixel took 4.5 ms at 4K. On an Apple M3 Max a 4K frame through the C API takes 2.7 ms to RGB instead of 7.6 ms, and 1.2 ms to planes instead of 1.6 ms; at 1080p 0.8 and 0.5 ms instead of 2.2 and 1.1 ms.
emberwave-cli decodegains as much. A failedemberwave_decoder_decode_yuvor_decode_rgbmay now have written some rows.
Fixed
- The browser pages, from a review of them (decision D39):
- The benchmark page gave times for frames the GPU never decoded when the browser reported an error; it now fails that size with the error.
- The player held a file about twice in memory, and a file with millions of empty frames could panic the module, after which nothing worked until the page was reloaded. It now keeps one copy, refuses frames too short to decode, plays the frames before one that is cut off rather than nothing, and loads files of up to 1 GiB and pictures of up to 8192x4320 pixels' worth.
- After a lost GPU the next file gets a new device; after a panic the page asks to be reloaded.
- The benchmark frees its GPU device and wake lock however a run ends, marks a run during which the page was hidden, and no longer blocks for seconds on a coarse timer.
- Errors from the browser show their message without the JavaScript stack.
- The decoder makes its read-back buffers only when a decode first reads back: a player, in a browser or natively, saves 43.5 MiB at 4K.
emberwave v0.7.0
Apps on iPhones, iPads and Macs: the C library as an XCFramework that Swift imports, checked in an iOS Simulator, where contexts can now be created. Also a benchmark page for browsers, phones included, a faster CPU side of decoding, and fixes from a review of the command line tool.
Added
- A benchmark page for browsers,
bench.htmlnext to the player: it encodes test streams at 1080p, 1440p and 4K in the page and times their decoding to RGB in GPU memory frame by frame, on any device with WebGPU, a phone included, and gives a report to copy. - The C library for apps on iPhones, iPads and Macs, as
Emberwave.xcframework(decision D35, #52). Each release has it asemberwave-ffi-VERSION-xcframework.zip. It holds static libraries for devices (iOS 12 or later), for the iOS Simulator on Apple silicon and for Macs with Apple silicon, with the header and a module map, so that Swift canimport Emberwave.scripts/build_xcframework.shbuilds it, andcrates/emberwave-ffi/examples/swiftis the decoding example in Swift. CI runs the C and Swift examples, linked with it, on a Mac and in an iOS Simulator.
Changed
- Decoding is faster on the CPU's side (decision D34, #45): the decodes that wait for their frame write it into an upload buffer the decoder keeps rather than through
Queue::write_buffer, which takes new memory for every frame. On an Apple M3 Max a 4K frame decodes to planes in about 0.89 ms instead of 1.06 ms, the CPU's part falling from 0.23 to 0.08 ms.encode_rgbstill writes through the queue.
Fixed
emberwave-cli decodereported success when writing a PNG failed, on a full disk or past a file size limit, and left an empty or cut file: the PNG writer was dropped without being finished. It now fails, naming the file.- Output piped into a program that stops reading, such as
emberwave-cli info FILE | head, made the command panic; it now ends quietly. - Errors of
emberwave-cliname the file they are about.comparereads both files frame by frame rather than whole,bench-encoderefuses a picture size the format cannot have before it reads the video, the Y4M reader reads no header or frame line longer than 64 KiB,compare --min-psnrrefuses NaN, which every PSNR passed, anddecodeof a PWV1 file without frames is an error, as it is forbenchandcompare. FrameAssemblerignores a block of an older frame even when its size is wrong, as upstream does, rather than rejecting the packet.- No context could be created where the adapter falls short of a WebGPU default limit that emberwave does not need. Metal gives the iOS Simulator, and Apple GPUs before the A11, 15 inter-stage variables, one fewer than the default. A context now asks for no limit beyond the adapter's (decision D36, #54).
emberwave v0.6.0
Encoding for hosts: the encoder in the C API, with planes at any row stride and packets for the network, and in Rust from pictures that are on the GPU already, such as a screen capture's.
Added
- An encoder in the C API (#42):
emberwave_encoder_create,_encode,_infoand_destroyencode planes with row strides into a frame in the caller's buffer, with the colour signalling ofEmberwaveSignalling, andemberwave_frame_packetssplits a frame into packets for a decoder (decision D33).examples/c/encode.cshows them in use. gpu::Encoder::encode_stridedencodes planes whose rows are a stride apart, and a stride below the width is the newEncodeError::PlaneStride.- Encoding pictures that are on the GPU already (#33):
gpu::Encoder::encode_texturestakes aTexturePicture, threeR8Unormtextures or NV12's layout of anR8Unormand anRg8Unormtexture, and makes the bytesencodemakes of the same picture in CPU memory, without the CPU writing the planes (decision D32). An unsuitable texture is the newGpuError::Texture.emberwave-cli bench-encode --gpu --texturestimes it.
Changed
GpuErrorhas a new variant,Texture, andEncodeErrorone,PlaneStride; amatchover every variant needs arms for them.- A context asks for the adapter's largest 2D textures, as it asks for its largest buffers, so that the textures of pictures to encode can be as large as the pictures; one limited to WebGPU's guarantees keeps 8192.
emberwave v0.5.0
The GPU encoder overtakes upstream's: on an Apple M3 Max it encodes a 4K frame in about 1.9 ms, against 3.4 ms for upstream's encoder at the same precision, and bench-encode measures both. Making a decoder or an encoder on a lost device, and making contexts on several threads with some Linux Vulkan loaders, no longer crash.
Added
- Encoder benchmarks (#29):
emberwave-cli bench-encodetimes an encoder on the frames of a Y4M video, in the waybenchtimes the decoder, andEncoder::encode_timedreturnsEncodeTimingswith the GPU time of each stage.refgen bench-encodetimes upstream's encoder the same way, at both of its precisions, andscripts/bench.shputs them inBENCHMARKS.md, with the pictures fromscripts/gen_bench_data.sh --sources.
Changed
- The GPU encoder is faster (decision D30): on an Apple M3 Max a 4K frame encodes in about 1.9 ms instead of about 3.5 ms, against 3.4 ms for upstream's encoder at the same precision. The planes go through an upload buffer the encoder keeps from frame to frame, 12 MB of GPU memory at 4K that the old path allocated anew for every frame, and the quantisation adds each subblock's errors in registers. The CPU encoder adds its errors in the same grouping; its streams are the same as before.
Fixed
Decoder::time_framesometimes reported the previous frame's time for its last stages, or none at all, on Metal: the timestamps were resolved in the frame's command buffer, before the GPU had written them. They are now resolved once the frame is done, in a submission of their own, whichgpu_walldoes not include.gpu::Decoder::newandgpu::Encoder::newpanicked inside wgpu when the context's device had been lost, a destroyed device for one (#35). They now return an error,GpuError::DeviceLostas decoding and encoding do, andemberwave_decoder_createin the C API returnsEMBERWAVE_STATUS_DEVICE_LOSTrather thanEMBERWAVE_STATUS_INTERNAL.- On Linux with a Vulkan loader from 1.3.284 to 1.4.344, such as Debian 13's or Ubuntu 26.04's, creating contexts on several threads at once could crash the process inside the loader (#36).
GpuContext::newandemberwave_context_createnow create one context at a time (decision D31).
emberwave v0.4.0
emberwave encodes: an encoder on the CPU and on the GPU whose streams upstream's decoder reads, at upstream's PSNR or above for the same bytes. On an Apple M3 Max the GPU encodes a 4K frame in about 4 ms, the CPU in about 240 ms.
Added
- An encoder on the CPU (#14):
emberwave_core::encode::Encoderturns 8-bit pictures into frames within a byte budget, andemberwave-cli encodeturns a Y4M video into a PWV1 file. Upstream's decoder reads its streams; at the same bytes its PSNR is upstream's encoder's or higher. It makes the same bytes on every system. - Writers for the formats the parsers read:
BlockHeader::to_words,SequenceHeader::start_of_frame_wordsandstream_words, andPwv1Header::to_bytes. - Test vectors of the encoder in
testdata/encoder/, with upstream's decode of them, andscripts/gen_encoder_vectors.shto make them.refgen encodecan write its pictures (--source) and encode a Y4M file (--input). - An encoder on the GPU (#14):
gpu::Encoderruns the CPU encoder's steps as compute shaders, the forward transform, quantisation, rate control and packing, with one upload and one read-back per frame. Its frames are the CPU encoder's, or differ in some blocks where the GPU rounds a coefficient differently, at the same PSNR; on an Apple M3 Max the committed vectors come out byte for byte, and a 4K frame takes about 4 ms.emberwave-cli encode --gpuuses it, with--backendand--softwareas for decoding.
Changed
emberwave-cli's Y4M reader keeps the range, the chroma siting and the frame rate of the header, readsC420paldvas chroma on the left, and reads a file a frame at a time where that is enough.GpuErrorhas a new variant,Encode, for the GPU encoder's refusals; amatchover every variant needs an arm for it.emberwave-cli encodeends with the mean, median and 99th percentile of the time per frame instead of the mean alone.
emberwave v0.3.0
emberwave outside Rust: a player in the browser, published at https://with-developer.github.io/emberwave/, and a C API with prebuilt libraries for Linux, macOS and Windows.
Added
- A browser player (#15):
crates/emberwave-weband the page inweb/, published to GitHub Pages, decode.pwv1files with WebGPU in the page and draw them on a canvas. Decoder::encode_rgbrecords a decode into the caller's command encoder without waiting, andDecoder::rgba_buffergives the picture;GpuContext::from_adaptercreates a context on an adapter the caller chose. Together they let a player keep the picture on the GPU, and they are what the browser player uses.- A C API (#16):
crates/emberwave-ffiwith the headerinclude/emberwave.h, made by cbindgen. Contexts and decoders, packets pushed as they arrive, and the decoded planes or RGB written into the caller's memory; status codes with a message per thread, and panics caught at the boundary. Each release ships the header with a shared and a static library for Linux, macOS and Windows, and a C example decodes every test vector in CI on all three. Decoder::clear,Decoder::start_of_frameandDecoder::lost, which the C API needed.
Changed
GpuContext::new, which blocks, is not part of the WebAssembly build; useGpuContext::new_asyncorGpuContext::from_adapterthere.- A
Decoderdestroys its GPU buffers when it is dropped. Native wgpu freed them then anyway; in a browser they waited for the garbage collector. A clone of the buffer fromDecoder::rgba_bufferdoes not keep it alive.
emberwave v0.2.0
Faster decoding: on an Apple M3 Max a 4K frame decodes to planes in 1.11 ms instead of 4.28 ms (1.37 instead of 4.53 ms with the conversion to RGB), 1.4 times the time of upstream's own Metal decoder at FP32 (#11, #12). The decoded pictures are the same up to rounding ties: in the test vectors 5 of 464,790 samples now differ by one level, as the compiler fuses a multiply and an add differently in the rewritten transform.
Changed
- The inverse transform and the dequantiser keep the values of an invocation in registers, written out with constant indices: the transform is 4.5 times faster.
- The dequantiser finds the band of a block by binary search. With the change above it is 2.6 times faster.
- The last level of the inverse transform writes the 8-bit planes itself. The separate packing pass and the float planes (47.5 MiB at 4K) are gone.
- The frame assembler validates blocks without building their full structure, 2.8 times faster.
- Workgroup memory is no longer zeroed before every workgroup, which the shaders never needed: the inverse transform is another 1.7 times faster. On WARP, Direct3D's software rasteriser, the first frame no longer takes a minute and a half.
- Software rendering on lavapipe is about a third faster too (4K: 46 ms instead of 69 ms).
Removed
StageTimings::pack: there is no packing pass any more, and the time of the 8-bit output is part ofidwt.