createGpuTexture function
Creates a flutter_gpu texture together with the single handle that will ever stand for it.
The only place in lib/ that constructs a TextureHandle. That is what
makes the identity contract hold rather than merely be documented: this
returns the handle, the gpu.Texture never escapes as a value, and so no
call site is in a position to wrap one a second time. identical() on two
handles therefore answers the question callers actually mean.
The argument list mirrors gpuContext.createTexture — including its
defaults — so a call site that moves here reads the same.
type is passed through rather than inferred. flutter_gpu derives a
multisample type from sampleCount on its own, but a cube is not something
any other argument implies, and it changes what the texture is:
gpu.Texture.sliceCount becomes six, and overwrite starts taking a face
index. A cube also asks for enableRenderTargetUsage false at every call
site there is — nothing renders into a face, because ColorTarget has no
slice to render into.
Implementation
TextureHandle createGpuTexture(
StorageMode storageMode,
int width,
int height, {
TextureFormat format = TextureFormat.r8g8b8a8UNormInt,
int sampleCount = 1,
bool enableRenderTargetUsage = true,
bool enableShaderReadUsage = true,
int mipLevelCount = 1,
TextureType type = TextureType.texture2D,
}) {
final texture = gpu.gpuContext.createTexture(
storageMode.toGpu(),
width,
height,
format: format.toGpu(),
// Multisampling still decides the type when the caller has no opinion, and
// that is not a nicety — passing a plain `texture2D` with a sample count
// above one is a combination flutter_gpu refuses, and it refuses it as
// "Texture creation failed" with nothing about which argument was wrong.
// This line used to be absent for exactly that reason; a cube is the first
// type no other argument implies, so the type is passed *and* the old
// inference is kept.
textureType: type == TextureType.texture2D && sampleCount > 1
? gpu.TextureType.texture2DMultisample
: type.toGpu(),
sampleCount: sampleCount,
// Clamped to what the device will allocate. `fullMipCount` stops one short
// of one-by-one, and asking for more than it throws — so the trim happens
// here, where the limit is, rather than at every call site.
mipLevelCount: mipLevelCount <= 1
? 1
: (mipLevelCount < gpu.Texture.fullMipCount(width, height)
? mipLevelCount
: gpu.Texture.fullMipCount(width, height)),
enableRenderTargetUsage: enableRenderTargetUsage,
enableShaderReadUsage: enableShaderReadUsage,
);
return TextureHandle(
backend: texture,
width: width,
height: height,
format: format,
sampleCount: sampleCount,
storageMode: storageMode,
type: type,
);
}