Skip to content

[Impeller] Find a way to use half-precision samplers in metal #123409

@jonahwilliams

Description

@jonahwilliams

Given the following source shader:

#include <impeller/types.glsl>

uniform sampler2D texture_sampler;

uniform FragInfo {
  float alpha;
}
frag_info;

in vec2 v_texture_coords;

out vec4 frag_color;

void main() {
  vec4 sampled = texture(texture_sampler, v_texture_coords);
  frag_color = sampled * frag_info.alpha;
}

We'd like to produce the following metal shader


#include <metal_stdlib>
#include <simd/simd.h>

using namespace metal;

struct FragInfo
{
    half alpha;
};

struct texture_fill_fragment_main_out
{
    half4 frag_color [[color(0)]];
};

struct texture_fill_fragment_main_in
{
    half2 v_texture_coords [[user(locn0)]];
};

fragment texture_fill_fragment_main_out texture_fill_fragment_main(texture_fill_fragment_main_in in [[stage_in]], constant FragInfo& frag_info [[buffer(0)]], texture2d<half> texture_sampler [[texture(0)]], sampler texture_samplerSmplr [[sampler(0)]])
{
    texture_fill_fragment_main_out out = {};
    out.frag_color = texture_sampler.sample(texture_samplerSmplr, in.v_texture_coords) * frag_info.alpha;
    return out;
}

I've tried two different techniques:

  1. Using defines and 16bit extensions in GLSL. This allows us to write float16_t, f16vecN types
  2. Using Spirv-tools relaxed precision passes. These automatically convert relaxed precision to half where possible.

Unfortunately this has run into problems. Using the extension GL_AMD_gpu_shader_half_float_fetch I can conditionally define a f16sampler2d type that I would expect could get compiled to a sampler<half>. However, this causes an internal compile error because it may not be able to be expressed in Vulkan.

> ../../flutter/impeller/entity/shaders/texture_fill.frag: GLSL to SPIRV failed; Compilation error. 0 error(s) and 0 warning(s).
shaderc: internal error: compilation succeeded but failed to optimize: [VUID-StandaloneSpirv-OpTypeImage-04656] Expected Sampled Type to be a 32-bit int, 64-bit int or 32-bit float scalar type for Vulkan environment
  %12 = OpTypeImage %half 2D 0 0 0 1 Unknown

If instead, I convert everything else to half precision and try to rely on the relaxed to half passes, the sampler still isn't converted resulting in the following:

#include <metal_stdlib>
#include <simd/simd.h>

using namespace metal;

struct FragInfo
{
    half alpha;
};

struct texture_fill_fragment_main_out
{
    half4 frag_color [[color(0)]];
};

struct texture_fill_fragment_main_in
{
    half2 v_texture_coords [[user(locn0)]];
};

fragment texture_fill_fragment_main_out texture_fill_fragment_main(texture_fill_fragment_main_in in [[stage_in]], constant FragInfo& frag_info [[buffer(0)]], texture2d<float> texture_sampler [[texture(0)]], sampler texture_samplerSmplr [[sampler(0)]])
{
    texture_fill_fragment_main_out out = {};
    out.frag_color = half4(texture_sampler.sample(texture_samplerSmplr, float2(in.v_texture_coords))) * frag_info.alpha;
    return out;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Issues that are less important to the Flutter projecte: impellerImpeller rendering backend issues and features requestsengineflutter/engine related. See also e: labels.

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions