Skip to content

TSL: Add explicit texture sampling functions #31850

@shotamatsuda

Description

@shotamatsuda

Description

The internal state of TextureNode can conflict with other state (e.g. biasNode, gradNode, and compareNode) and lead to unexpected code being generated. For example:

const sampleNeighbors = (textureNode, coord) => {
  // The texture nodes below will "sample" instead of "load" with a wrong 
  // interpretation of "coord", because the "textureNode" has a "biasNode":
  const tex1 = textureNode.load(coord)
  const tex2 = textureNode.offset(ivec2(0, 1)).load(coord)
  ...
}

const textureNode = texture(image).bias(0.1)
const neighbors = sampleNeighbors(textureNode, screenCoordinate)

The code below doesn't solve this because texture() copies biasNode as well:

const tex1 = texture(textureNode).load(coord) // This will still "sample".

This also doesn't solve if we mutate dependencies in place.

const tex1 = textureNode.getBase().load(coord) // This will still "sample".

// In another place:
textureNode.biasNode = float(0.1)

The following is also problematic because it breaks the reference in value, so later updates to textureNode.value are not reflected.

const tex1 = texture(textureNode.value).load(coord) // Out of sync with "textureNode".

// In another place:
textureNode.value = anotherImage

Solution

Introducing explicit functions like below could solve this issue. The names can be adjusted to better align with other TSL functions (textureSize appears to follow GLSL rather than WGSL).

  • textureLoad(textureNode, coords, [level = 0])
  • textureSample(textureNode, coords, [offset])
  • textureSampleBias(textureNode, coords, bias, [offset])
  • textureSampleGrad(textureNode, coords, ddx, ddy, [offset])
  • textureSampleLevel(textureNode, coords, level, [offset])
  • textureGather(textureNode, coords, [component = 0], [offset])
  • textureGatherCompare(textureNode, coords, depthRef, [offset])
  • textureSampleCompare(textureNode, coords, depthRef, [offset])
  • textureSampleCompareLevel(textureNode, coords, depthRef, [offset])

Alternatives

  • Clear internal fields that conflict with the operation implied by the most recent call to TextureNode. (e.g. clear biasNode, gradNode when load() is called). This would be a promising alternative.
  • Add a method like TextureNode.reset() to create a fresh TextureNode with a clean state.
  • Refactor the conditionals in TextureNode.generateSnippet(), but I doubt it would fully address the issue.

Additional context

Below is a list of texture-related WGSL and GLSL functions and their implementation status in TSL. This hopefully shows the complexity of mapping the internal state to specific sampling functions.

Load

TSL .level(level).load(coords)
WGSL textureLoad(texture, coords, level)
GLSL texelFetch(sampler, coords, level)

Load with offset

TSL .offset(offset).load(coords)
WGSL No built-in function
GLSL texelFetchOffset(sampler, coords, level, offset)

Sample

TSL .sample(coords)
WGSL textureSample(texture, sampler, coords)
GLSL texture(sampler, coords)

Sample with offset

TSL .offset(offset).sample(coords)
WGSL textureSample(texture, sampler, coords, offset)
GLSL textureOffset(sampler, coords, offset)

Sample with bias

TSL .bias(bias).sample(coords)
WGSL textureSampleBias(texture, sampler, coords, bias)
GLSL texture(sampler, coords, bias)

Sample with bias and offset

TSL .bias(bias).offset(offset).sample(coords)
WGSL textureSampleBias(texture, sampler, coords, bias, offset)
GLSL texture(sampler, coords, offset, bias)

Sample with grad

TSL .grad(ddx, ddy).sample(coords)
WGSL textureSampleGrad(texture, sampler, coords, ddx, ddy)
GLSL textureGrad(sampler, coords, ddx, ddy)

Sample with grad and offset

TSL .grad(ddx, ddy).offset(offset).sample(coords)
WGSL textureSampleGrad(texture, sampler, coords, ddx, ddy, offset)
GLSL textureGradOffset(sampler, coords, ddx, ddy, offset)

Sample with level

TSL .level(level).sample(coords)
WGSL textureSampleLevel(texture, sampler, coords, level)
GLSL textureLod(sampler, coords, level)

Sample with level and offset

TSL .level(level).offset(offset).sample(coords)
WGSL textureSampleLevel(texture, sampler, coords, level, offset)
GLSL textureLodOffset(sampler, coords, level, offset)

Gather four texels

TSL Not implemented
WGSL textureGather(component, texture, sampler, coords)
GLSL textureGather(sampler, coords, component)

Gather four texels with offset

TSL Not implemented
WGSL textureGather(component, texture, sampler, coords, offset)
GLSL textureGatherOffset(sampler, coords, offset, component)

Gather and compare four texels

TSL Not implemented
WGSL textureGatherCompare(texture, sampler, coords, ref)
GLSL textureGather(sampler, coords, ref)

Gather and compare four texels with offset

TSL Not implemented
WGSL textureGatherCompare(texture, sampler, coords, ref, offset)
GLSL textureGatherOffset(sampler, coords, ref, offset)

Compare

TSL .compare(ref).sample(coords)
WGSL textureSampleCompare(texture, sampler, coords, ref)
GLSL texture(sampler, (coords, ref))

Compare with offset

TSL .offset(offset).compare(ref).sample(coords)
WGSL textureSampleCompare(texture, sampler, coords, ref, offset)
GLSL textureOffset(sampler, (coords, ref), offset)

Compare with level

TSL .level(level).compare(ref).sample(coords)
Note: Not tested.
WGSL textureSampleCompareLevel(texture, sampler, coords, ref)
Note: Level is fixed to 0 syntactically.
GLSL textureLod(sampler, (coords, ref), level)

Compare with level and offset

TSL .level(level).offset(offset).compare(ref).sample(coords)
Note: Not tested.
WGSL textureSampleCompareLevel(texture, sampler, coords, ref, offset)
Note: Level is fixed to 0 syntactically.
GLSL textureLodOffset(sampler, (coords, ref), level, offset)

Query samples

TSL Not implemented
WGSL textureNumSamples(texture)
GLSL textureSamples(sampler)

Query levels

TSL Not implemented
WGSL textureNumLevels(texture)
GLSL textureQueryLevels(sampler)

Query layers

TSL Not implemented
WGSL textureNumLayers(texture)
GLSL textureSize(sampler).z

Query size

TSL .size(level)
WGSL textureDimensions(texture, level)
GLSL textureSize(sampler, level)

Metadata

Metadata

Assignees

No one assigned

    Labels

    TSLThree.js Shading Language

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions