flutter3d_hardware 0.8.0 copy "flutter3d_hardware: ^0.8.0" to clipboard
flutter3d_hardware: ^0.8.0 copied to clipboard

The graphics vocabulary flutter3d is written against: formats, handles, a command encoder and a device. Names no graphics API. No Flutter, no renderer.

0.8.0 #

Breaking: PassEncoder.bindTexture returns bool. False when the stage declares no such sampler, never a throw, asked of the stage and not of the program; a backend with no reflection answers true, as it does for bindUniformBlock. Every implementation of PassEncoder changes its signature. Until now each backend answered a slot the stage lacks its own way: Impeller threw, WebGL and WebGPU did nothing, the software rasteriser bound it.

bindPipeline forgets every binding, on every backend. Uniform blocks, textures, vertex slots and the index buffer, as clearBindings does, even when the same pipeline is bound again. The contract said a backend was "free to" drop them, and one kept them, which hid a missing bind there.

A declared slot left unbound is the caller's mistake, named by a backend that can see it and never served another draw's resource.

Breaking for a backend: GraphicsDevice has eleven new members, the whole of the 0.8 cycle's contract declared at once: supportsGpuTimestamps, onGpuTimings, supportsCompute, createStorageBuffer, createComputePipeline, beginComputePass, readBuffer, releaseStorageBuffer, supportsFloat32Filtering, supportsIndependentBlend and hdrOutputFormats. Each backend is its own package on a caret range of this one, so a member added in 0.8.1 would break every backend published before it; declaring them all now means no patch has to. A class that implements the interface outside this repository stops compiling until it answers all eleven, and answering false or empty and throwing UnsupportedError from the creators is a complete answer. Code that only calls a device compiles as it did.

Compute has a shape. StorageBuffer, ComputePipelineHandle and ComputeEncoder (bindPipeline, bindStorageBuffer, bindUniformBlock, dispatch, submit) follow the render pass's rules: a binding the stage does not declare answers false, and bindPipeline forgets every binding. readBuffer reads back a buffer created with hostReadable: true. WebGPU and the software rasteriser run it in 0.8.0; Impeller and WebGL2 answer supportsCompute with false, so ask it first. H6

A pass can say how long the GPU spent in it. RenderPassDescriptor.label names a pass to a GPU debugger and to onGpuTimings, whose listener gets a GpuFrameTimings (the frame's number and a GpuPassTiming per labelled pass, in microseconds) a frame or two after the frame was drawn, since a timestamp can only be read once the GPU has written it. Only WebGPU answers supportsGpuTimestamps with true, where the adapter grants timestamp-query. A backend with nowhere to put a label ignores it. H2

A pass can load the depth an earlier pass stored. DepthTarget.loadAction and DepthTarget.storeAction default to LoadAction.clear and StoreAction.dontCare, which is what every pass did before, so a descriptor that names neither opens the same pass. Weighted blended transparency is the one caller: its transparent draws test against the depth the opaque pass left. A texture another pass loads must be stored and must not be StorageMode.deviceTransient, which on Apple GPUs has no memory to load from.

setBlend's attachment index is honoured where supportsIndependentBlend is true: Impeller, WebGPU, the software rasteriser for its first two attachments, and WebGL2 with OES_draw_buffers_indexed. Elsewhere the index is ignored and attachment zero takes the state, as before. A caller that wants two attachments blended differently sets attachment zero first and the others after it, which draws the same pass on all four backends.

A stage says what its compiled function kept. ShaderHandle.kept (a StageBindings: the blocks and samplers the compiler left a slot for) and ShaderHandle.layouts (each block's members as UniformMemberLayouts) are filled by every backend for the engine's own stages, from tables in flutter3d_shaders that a test holds to a fresh compile. An encoder refuses a block or sampler the stage dropped before anything reaches the driver, which makes the 0.7.1 crash (an unlit draw handed a light list its Metal function had no slot for) impossible whatever a caller asks. mayBindBlock and mayBindSampler ask the same question. A stage from an application's own bundle has neither table and is null in both.

A uniform block can be one object. UniformBlock is the base of the classes flutter3d_shaders now generates, one per block with a preallocated Float32List per member, so a caller fills fields the compiler checks and builds no map per draw. PassEncoder.bindBlock(stage, block) binds one with only the members that stage's layout has, since one block name can be wider in one stage than another. bindUniformBlock and its map are unchanged. H1

package:flutter3d_hardware/trace.dart, new: a frame kept as a file. RecordingDevice wraps any GraphicsDevice, passes every call through and keeps it as a TraceEvent, with resources named by creation order and every payload copied. Trace encodes and decodes the .f3dtrace format (a JSON header and one blob), and replayTrace issues a trace against another device and returns a TraceReplay with what it read back. TraceEvent is sealed and has a variant for every call of the contract, compute included. It is a library of its own because an application draws without it; recording costs a copy of every upload and uniform block for as long as the events are kept. H3

FakeBackend takes stageBindings, a table of what each stage declares, and holds binds to it: false for a slot a stage lacks, and every declared slot a draw leaves unbound in bindingViolations. RecordedTexture records the stage it was bound through, and FakePass records a copy of each bound block's members, as every real backend copies at the bind: a test that refilled one block object per draw used to see the last draw's values in every recorded bind. FakeBackend answers false or empty to the eleven new members.

Its flutter3d_* dependencies ask for ^0.8.0.

0.7.1 #

Released with the rest of the stack at 0.7.1. Nothing in this package changed. The release it resolves against builds from pub.dev again and no longer crashes Metal on the first unlit draw.

Its flutter3d_* dependencies ask for ^0.7.1, and it asks for vector_math ^2.4.3.

0.7.0 #

Breaking. GraphicsDevice.present is gone. It was the one member that returned a Flutter Widget, and the one reason this package depended on Flutter at all. FakeBackend's own override, a bare throw UnsupportedError, is deleted rather than migrated. Nothing else in this package names Flutter; it resolves on the Dart VM alone now, and its tests run under plain dart test.

registerBackendOpener/openRegisteredDevice and registerDevicePresenter/lookUpDevicePresenter, new. What replaced present is a registry a backend adds itself to rather than a fixed list an assembly layer would have to already know every entry of — a hardware abstraction layer that a new backend could only extend by editing another package's source was not one. Opening a device names no framework, so the whole of that registry lives here, typed; presenting one returns a Flutter Widget, which this package still must not name, so only the generic half — registered and looked up by a device's own runtime Type, holding whatever a caller registered as Object — lives here. flutter3d_app supplies the typed FramePresenter shape and the cast back to it for the four backends this repository ships, and is not required for a fifth.

Breaking for a backend, and for nobody else. GraphicsDevice has three new abstract members, overwriteGeometry, overwriteTexture and maxColorAttachments. A class that implements the interface outside this repository stops compiling until it answers all three. Code that only calls a device compiles as it did.

overwriteGeometry writes into a buffer the device already made. It takes the target GeometryBuffer, an offset in bytes and the bytes, and throws ArgumentError when offsetInBytes + bytes.lengthInBytes runs past target.lengthInBytes. The offset is measured inside the view it is given, so a write through GeometryBuffer.slice() lands where the slice says. The new bytes are visible from the next pass; a pass already encoded draws what it was handed. Before this the only way to change a vertex was uploadGeometry again.

overwriteTexture patches a rectangle of a texture's base level. region is a ScreenRect and defaults to the whole texture, and the bytes are RGBA8, row-major from the top, the way readback returns them. It accepts the two formats in readbackFormats and throws UnsupportedError for any other, compressed ones included, and for a mipLevel other than zero: patching one level of a chain would leave the rest stale with nothing saying so. It returns a Future because flutter_gpu can only overwrite a whole level, so that backend reads the level back, patches it and writes it again; the other three complete in the same turn.

maxColorAttachments, and a pass that asks for more throws. On Impeller's OpenGL ES path a second colour attachment reaches an FML_CHECK and the process stops, in release too. A device now says how many it can open, and RenderPassDescriptor.checkAttachmentLimit(limit, backend:) is what each backend calls on the way into beginRenderPass, so all four refuse with the same UnsupportedError, which names the backend and the limit.

A registration can be taken out again. registerBackendOpener returns a BackendRegistration and registerDevicePresenter a PresenterRegistration, each with undo(). A test suite run in one process kept a fake backend registered for every file after the one that registered it, and an empty frame compares equal to another empty frame. Undoing a fallback puts back the fallback it displaced, and undo() does nothing once a later registration has replaced the one it belongs to. Callers that ignored the old void return are unaffected.

FakeBackend follows all of it. It records overwrites and overwrittenTextures with the same bounds and format checks a real device makes, takes maxColorAttachments in its constructor with a default of 2, and supportsOffscreenMsaa is a field a test can set to false.

The archive carries a skill. skills/flutter3d-hardware-backend-contract/ is a SKILL.md for a coding agent that writes a backend or calls GraphicsDevice directly. A project depending on this package installs it with dart run skills@ get.

0.6.0 #

  • Nothing of its own changed. Every file under lib/ is byte for byte what 0.5.1 put on pub.dev — the vocabulary, the fourth bundle section and the format version included. This package depends on no sibling, so it has no floor to move either; it takes the set's number because four backends and an engine now declare ^0.6.0 against it, and one number naming one tree is what makes that declaration mean the combination CI actually resolved.
  • Nothing is deprecated, renamed or removed. Code written against 0.5.1 compiles unchanged.

0.5.1 #

  • ShaderBundle.webgpuSection, and the format version stays at 1. A fourth backend needs a fourth section, and the container was already built to take one: the section table is a count followed by that many name-to-bytes pairs, and the only version gate is equality, so a bundle carrying WGSL is read by the Impeller and WebGL backends exactly as it was before — they count four, take theirs, and carry the rest through untouched. The doc comment says the two things the neighbouring sections do not have to. First, the payload holds reflection beside the code — uniform block names with member offsets, group-and-binding pairs for textures and samplers, attribute locations by name — because a compiled WGSL module will not answer where anything landed, and code without those answers is code a device can compile and cannot bind. Second, the payload versions itself, separately, because the shape of that reflection is an agreement between one packer and one backend and will move again while the container does not.
  • The SDK check is false for WGSL, on purpose. compiledFor compares the token impellerc was pinned to; there is no impellerc in the WebGPU path at all, because WGSL is text the browser compiles when the page loads. A bundle carrying only that section leaves sdk empty and the check answers false forever. Written down so the next reader does not take it for an oversight and invent a version to compare.
  • WindingOrder says the winding is measured in clip space. No behaviour changes; the sentence is one a reader otherwise pays for in test triangles, because a transform that mirrors flips the facing without touching the order the vertices were given in.

0.5.0 #

  • No API change. Released with the set so every floor in the workspace moves together; see doc/boundary-0.5.0.md for what the release is.

0.4.2 #

  • bindUniformBlock's contract says which of the two failures is an error. A block the compiler dropped is false; a block missing a member the caller named throws. The doc comment used to promise the opposite for members, with a reason that did not hold, and the two hardware backends had drifted apart under it. A backend with no reflection of its shaders is not asked to tell the two apart — the software rasteriser hands the block to a Dart shader that looks up what it needs by name — and the conformance suite asks a backend which kind it is rather than assuming.
  • SamplerOptions.anisotropy and GraphicsDevice.maxAnisotropy. A floor seen along its length covers a footprint a few texels tall and many wide, and a trilinear sampler picks one level for the whole of it — the level that stops the long axis aliasing blurs the short one. The field is a count of taps along that long axis, one by default so every picture is the bytes it was, checked at construction to sit on a trilinear sampler because that is flutter_gpu's rule and the taps are taken across the chain, and clamped by every backend to what the device answers, so sixteen is a safe thing to ask for. The device getter is there to decide with rather than to guard: the bridge asks it once per level. FakeBackend answers sixteen and can be told otherwise. withAnisotropy copies a sampler with the one field a caller decides at run time.
  • GraphicsDevice.loadShaders: a shader bundle arrives as bytes. The one way a shader reaches a device without being an asset, for an editor that rebuilds a bundle and wants to see it without restarting and for an application that ships a look the engine never heard of and wants it on every backend. ShaderBundle is the container — a header naming the bundle, the SDK it was compiled on and the stages it claims, then one section per backend — with encode/decode checked against each other. LoadedShaderLibrary is what comes back: a ShaderLibrary with refresh, which reparses new bytes in place and keeps the identity of every handle already handed out. ShaderBundleRefused is the one exception a device answers when it will not load a bundle, and it names the bundle: never an empty library, which would fail at the first draw naming a stage rather than the file to rebuild.
  • FakeBackend.loadShaders and FakeLoadedShaderLibrary, which keep the same identity promise so a test of a reload path proves something; FakeBackend.linkedPipelines records every pair linked, in order, so a test can tell a frame that relinked from one that did not.
  • A loaded library lives as long as the device, and loadShaders now says so: there is no release, because the handles it handed out are held by whatever resolved them, so an application whose shaders change loads one bundle and refreshes it in place. LoadedShaderLibrary also says what a pipeline linked before a refresh does until it is dropped — draws the code it had, on every backend — since one backend did not keep that.
  • A refresh that drops a stage in use is refused, naming the stage. The other half of the identity promise, and LoadedShaderLibrary.refresh now says so: a handle is the renderer's for its lifetime, so a bundle that no longer names that stage would leave a live handle over nothing — a stale pipeline on one backend, a link error on another. Every backend and the fake refuse it the same way, before anything is swapped, and the conformance suite holds them to it.
  • ShaderBundle.decode refuses a string field that is not UTF-8 as a ShaderBundleRefused, rather than letting the decoder's own FormatException out as the one exception that was not a refusal.
  • package:flutter3d_hardware/shader_bundle.dart exports the container on its own, with no Flutter behind it: the barrel reaches package:flutter through GraphicsDevice, and the tool that packs a bundle is a dart run script.
  • GraphicsDevice.readback. The pixels of a texture, or a region of it, as the passes submitted before the call left them, answered without stalling on the GPU — a copy queued in order and a future that resolves when the queue reports it done, a frame or two later. readPixels stays for the golden run and the probes, where the caller has stopped drawing and can afford to wait; this is for a caller still drawing that wants last frame's answer while this frame goes on: an exposure meter, an editor's pick. What cannot be read — tile memory, a multisampled target, a cube, a region past the edge — is refused with an ArgumentError by readbackRegionOf, once, so every backend refuses alike. A backend outside this repository has to add the member.
  • A readback is linear eight-bit RGBA or it is refused. readbackFormats names two layouts — r8g8b8a8UNormInt and b8g8r8a8UNormInt, the same four bytes in either order — and readbackRegionOf refuses any other format by name. The contract promises the same bytes on every backend, and a half-float target broke it three ways: on WebGL2 readPixels(RGBA, UNSIGNED_BYTE) of a float attachment is an INVALID_OPERATION that leaves the pack buffer at zeros and the future completing successfully with a black picture; flutter_gpu converted through toByteData; the software rasteriser clamped its floats. A float texture is read through readPixels, or drawn into an eight-bit target first, which is what the exposure meter's luminance pass is for.
  • The sRGB twins are refused too, with a message of their own. They were admitted on the grounds of being eight bits per channel, which is the wrong test: the question is whether three backends hand back the same bytes, and for an encoded texture nothing said they would — flutter_gpu reads through asImage().toByteData(rawRgba), which may hand back the linear values the encoding stands for, WebGL2 hands back what is stored, and the software rasteriser rounds its own floats. Nothing asked: every readback target the engine declares is r8g8b8a8UNormInt, so the two formats were a promise no conformance check ever made. The refusal points at the same texture's UNormInt layout rather than at drawing it into another target, because that is the different fix a different mistake needs.
  • FakeBackend.readback records what was asked — texture and region — and answers zeros unless answerReadback says otherwise, so a test can be the device that saw a dark frame or a particular id.
  • The stencil, whole. StencilState — compare, the three operations, two eight-bit masks — with StencilOperation and StencilFace mirrored from flutter_gpu value for value; PassEncoder.setStencil for one face or both and setStencilReference beside it; the same three fields on PassState, emitted after depth; and DepthTarget saying how its stencil is loaded, stored and cleared. A pass starts with the test off on both faces, whatever the pass before it set.
  • GraphicsDevice.supportsStencil, to ask before configuring a test against an attachment that has none, and TextureFormatStencil.hasStencil for the formats that carry one.
  • StencilState.narrowReference, which is where "eight bits" is decided once instead of three times. Left to themselves a software rasteriser wraps an out-of-range reference, GL clamps it and flutter_gpu forwards it, so the same value meant three things; every backend narrows through this now.
  • BlendState.keepDestination: zero from the source, one from the destination. flutter_gpu has no colour write mask and a discarded fragment writes no stencil, so this is the one way a draw marks the stencil and leaves the picture alone.
  • FakeBackend can be told it has no stencil, and FakePass records the stencil state and reference it was left with.
  • A colour attachment names a face and a level. ColorTarget.face picks a face of a cube in the order the uploads take them, ColorTarget.mipLevel a level below the base, and a pass's initial viewport covers the level. What a reflection probe is drawn into, and the first time anything in the engine renders into a face or a mip.
  • GraphicsDevice.createCubeRenderTarget allocates a cube a pass may draw into, device-private and empty, with a chain of the length asked for and trimmed to what the device will hold; supportsRenderToMip says whether a level below the base can be attached at all, which is the one capability that splits flutter_gpu by platform. FakeBackend records both and answers the capability either way.
  • PassEncoder.setBlendColor, and GraphicsDevice.supportsBlendColor beside it. Four BlendFactor values read a blend constant nothing could set, so a BlendState naming one threw on the software rasteriser and evaluated to zero on the other two — a plausible picture with a term missing from it. The setter is the missing half; the capability is there because flutter_gpu exposes no blend-constant setter at all, and a backend answering false must now refuse both the setter and a state naming one of the four rather than drawing it as zero. BlendState.usesBlendColor is where that list of four lives, so a backend cannot go on accepting a value the enum grows later.

0.4.1 #

  • GraphicsDevice.supportsTextureFormat. The question a block-compressed format needs asked and none of the backends was asking: every value of TextureFormat has a name everywhere, and BC is a desktop family, ETC2 a mobile one, ASTC newer still. A loader asks before it uploads and a no is a texture left out with a reason, not an ArgumentError out of an allocation. A backend outside this repository has to add the member; the three inside answer from flutter_gpu's capability, the WebGL2 context's extensions, and a constant no for anything compressed.
  • TextureFormatCompression: isCompressed and blockLayout for every block-compressed value, the one source every backend now reads.
  • FakeBackend records the mip chain each upload came with and can be told which formats to refuse.

0.4.0 #

  • A loan that outlives a trim goes back to the allocator. It used to be refiled under its retired pre-resize spec on release, where no acquire would ever match it — parked until the next trim. The pool now hands it straight back, keeps the throw-on-double-release contract, and has tests for the whole shape.

0.3.0 #

  • createCubeTextureFromPixels takes mip levels, which is what image-based lighting needs and what a hand-built chain has to be uploaded through.
  • LayeredShaderLibrary puts an application's own bundle in front of the engine's without replacing it.

0.2.0 #

  • PassEncoder split from CommandEncoder, so a contributor drawing into somebody else's pass cannot end it.
  • A render target pool that releases by identity and waits out the frames in flight, and capability questions a backend answers rather than guesses at.

0.1.0 #

  • The vocabulary an engine writes a frame in: texture and buffer handles, formats, render targets, pipelines, a command encoder and a shader library.

  • No implementation of its own. What it names is what a backend must answer to, and what the engine may assume.

  • SamplerOptions.anisotropy and GraphicsDevice.maxAnisotropy. A floor seen along its length covers a footprint a few texels tall and many wide, and a trilinear sampler picks one level for the whole of it — the level that stops the long axis aliasing blurs the short one. The field is a count of taps along that long axis, one by default so every picture is the bytes it was, checked at construction to sit on a trilinear sampler because that is flutter_gpu's rule and the taps are taken across the chain, and clamped by every backend to what the device answers, so sixteen is a safe thing to ask for. The device getter is there to decide with rather than to guard: the bridge asks it once per level. FakeBackend answers sixteen and can be told otherwise. withAnisotropy copies a sampler with the one field a caller decides at run time.

  • GraphicsDevice.loadShaders: a shader bundle arrives as bytes. The one way a shader reaches a device without being an asset, for an editor that rebuilds a bundle and wants to see it without restarting and for an application that ships a look the engine never heard of and wants it on every backend. ShaderBundle is the container — a header naming the bundle, the SDK it was compiled on and the stages it claims, then one section per backend — with encode/decode checked against each other. LoadedShaderLibrary is what comes back: a ShaderLibrary with refresh, which reparses new bytes in place and keeps the identity of every handle already handed out. ShaderBundleRefused is the one exception a device answers when it will not load a bundle, and it names the bundle: never an empty library, which would fail at the first draw naming a stage rather than the file to rebuild.

  • FakeBackend.loadShaders and FakeLoadedShaderLibrary, which keep the same identity promise so a test of a reload path proves something; FakeBackend.linkedPipelines records every pair linked, in order, so a test can tell a frame that relinked from one that did not.

  • A loaded library lives as long as the device, and loadShaders now says so: there is no release, because the handles it handed out are held by whatever resolved them, so an application whose shaders change loads one bundle and refreshes it in place. LoadedShaderLibrary also says what a pipeline linked before a refresh does until it is dropped — draws the code it had, on every backend — since one backend did not keep that.

  • A refresh that drops a stage in use is refused, naming the stage. The other half of the identity promise, and LoadedShaderLibrary.refresh now says so: a handle is the renderer's for its lifetime, so a bundle that no longer names that stage would leave a live handle over nothing — a stale pipeline on one backend, a link error on another. Every backend and the fake refuse it the same way, before anything is swapped, and the conformance suite holds them to it.

  • ShaderBundle.decode refuses a string field that is not UTF-8 as a ShaderBundleRefused, rather than letting the decoder's own FormatException out as the one exception that was not a refusal.

  • package:flutter3d_hardware/shader_bundle.dart exports the container on its own, with no Flutter behind it: the barrel reaches package:flutter through GraphicsDevice, and the tool that packs a bundle is a dart run script.

  • GraphicsDevice.readback. The pixels of a texture, or a region of it, as the passes submitted before the call left them, answered without stalling on the GPU — a copy queued in order and a future that resolves when the queue reports it done, a frame or two later. readPixels stays for the golden run and the probes, where the caller has stopped drawing and can afford to wait; this is for a caller still drawing that wants last frame's answer while this frame goes on: an exposure meter, an editor's pick. What cannot be read — tile memory, a multisampled target, a cube, a region past the edge — is refused with an ArgumentError by readbackRegionOf, once, so every backend refuses alike. A backend outside this repository has to add the member.

  • A readback is linear eight-bit RGBA or it is refused. readbackFormats names two layouts — r8g8b8a8UNormInt and b8g8r8a8UNormInt, the same four bytes in either order — and readbackRegionOf refuses any other format by name. The contract promises the same bytes on every backend, and a half-float target broke it three ways: on WebGL2 readPixels(RGBA, UNSIGNED_BYTE) of a float attachment is an INVALID_OPERATION that leaves the pack buffer at zeros and the future completing successfully with a black picture; flutter_gpu converted through toByteData; the software rasteriser clamped its floats. A float texture is read through readPixels, or drawn into an eight-bit target first, which is what the exposure meter's luminance pass is for.

  • The sRGB twins are refused too, with a message of their own. They were admitted on the grounds of being eight bits per channel, which is the wrong test: the question is whether three backends hand back the same bytes, and for an encoded texture nothing said they would — flutter_gpu reads through asImage().toByteData(rawRgba), which may hand back the linear values the encoding stands for, WebGL2 hands back what is stored, and the software rasteriser rounds its own floats. Nothing asked: every readback target the engine declares is r8g8b8a8UNormInt, so the two formats were a promise no conformance check ever made. The refusal points at the same texture's UNormInt layout rather than at drawing it into another target, because that is the different fix a different mistake needs.

  • FakeBackend.readback records what was asked — texture and region — and answers zeros unless answerReadback says otherwise, so a test can be the device that saw a dark frame or a particular id.

  • The stencil, whole. StencilState — compare, the three operations, two eight-bit masks — with StencilOperation and StencilFace mirrored from flutter_gpu value for value; PassEncoder.setStencil for one face or both and setStencilReference beside it; the same three fields on PassState, emitted after depth; and DepthTarget saying how its stencil is loaded, stored and cleared. A pass starts with the test off on both faces, whatever the pass before it set.

  • GraphicsDevice.supportsStencil, to ask before configuring a test against an attachment that has none, and TextureFormatStencil.hasStencil for the formats that carry one.

  • StencilState.narrowReference, which is where "eight bits" is decided once instead of three times. Left to themselves a software rasteriser wraps an out-of-range reference, GL clamps it and flutter_gpu forwards it, so the same value meant three things; every backend narrows through this now.

  • BlendState.keepDestination: zero from the source, one from the destination. flutter_gpu has no colour write mask and a discarded fragment writes no stencil, so this is the one way a draw marks the stencil and leaves the picture alone.

  • FakeBackend can be told it has no stencil, and FakePass records the stencil state and reference it was left with.

  • A colour attachment names a face and a level. ColorTarget.face picks a face of a cube in the order the uploads take them, ColorTarget.mipLevel a level below the base, and a pass's initial viewport covers the level. What a reflection probe is drawn into, and the first time anything in the engine renders into a face or a mip.

  • GraphicsDevice.createCubeRenderTarget allocates a cube a pass may draw into, device-private and empty, with a chain of the length asked for and trimmed to what the device will hold; supportsRenderToMip says whether a level below the base can be attached at all, which is the one capability that splits flutter_gpu by platform. FakeBackend records both and answers the capability either way.

0.4.1 #

  • GraphicsDevice.supportsTextureFormat. The question a block-compressed format needs asked and none of the backends was asking: every value of TextureFormat has a name everywhere, and BC is a desktop family, ETC2 a mobile one, ASTC newer still. A loader asks before it uploads and a no is a texture left out with a reason, not an ArgumentError out of an allocation. A backend outside this repository has to add the member; the three inside answer from flutter_gpu's capability, the WebGL2 context's extensions, and a constant no for anything compressed.
  • TextureFormatCompression: isCompressed and blockLayout for every block-compressed value, the one source every backend now reads.
  • FakeBackend records the mip chain each upload came with and can be told which formats to refuse.

0.4.0 #

  • A loan that outlives a trim goes back to the allocator. It used to be refiled under its retired pre-resize spec on release, where no acquire would ever match it — parked until the next trim. The pool now hands it straight back, keeps the throw-on-double-release contract, and has tests for the whole shape.

0.3.0 #

  • createCubeTextureFromPixels takes mip levels, which is what image-based lighting needs and what a hand-built chain has to be uploaded through.
  • LayeredShaderLibrary puts an application's own bundle in front of the engine's without replacing it.

0.2.0 #

  • PassEncoder split from CommandEncoder, so a contributor drawing into somebody else's pass cannot end it.
  • A render target pool that releases by identity and waits out the frames in flight, and capability questions a backend answers rather than guesses at.

0.1.0 #

  • The vocabulary an engine writes a frame in: texture and buffer handles, formats, render targets, pipelines, a command encoder and a shader library.
  • No implementation of its own. What it names is what a backend must answer to, and what the engine may assume.
1
likes
150
points
643
downloads

Documentation

API reference

Publisher

verified publisherpleion.dev

Weekly Downloads

The graphics vocabulary flutter3d is written against: formats, handles, a command encoder and a device. Names no graphics API. No Flutter, no renderer.

Homepage
Repository (GitHub)
View/report issues
Contributing

Topics

#graphics #gpu #rendering #abstraction

License

MIT (license)

Dependencies

vector_math

More

Packages that depend on flutter3d_hardware