1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
// Copyright (c) 2024 Xu Shaohua <shaohua@biofan.org>. All rights reserved.
// Use of this source is governed by Lesser General Public License that can be found
// in the LICENSE file.

#![allow(clippy::struct_excessive_bools)]

use crate::sksl::glsl::GLSLGeneration;
use crate::sksl::version::Version;

/// Indicates how GLSL must interact with advanced blend equations.
///
/// The KHR extension requires special layout qualifiers in the fragment shader.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub enum AdvBlendEqInteraction {
    /// No _blend_equation_advanced extension
    NotSupported,

    /// No interaction required
    Automatic,

    /// layout(blend_support_all_equations) out
    GeneralEnable,
}

#[derive(Debug)]
pub struct ShaderCaps {
    glsl_generation: GLSLGeneration,

    dual_source_blending_support: bool,
    shader_derivative_support: bool,

    /// Enables sampleGrad and sampleLod functions that don't rely on implicit derivatives
    explicit_texture_lod_support: bool,
    /// Indicates true 32-bit integer support, with unsigned types and bitwise operations
    integer_support: bool,
    non_square_matrix_support: bool,
    /// asinh(), acosh(), atanh()
    inverse_hyperbolic_support: bool,
    fb_fetch_support: bool,
    fb_fetch_needs_custom_output: bool,
    uses_precision_modifiers: bool,
    flat_interpolation_support: bool,
    no_perspective_interpolation_support: bool,
    sample_mask_support: bool,
    external_texture_support: bool,
    float_is_32_bits: bool,

    // isinf() is defined, and floating point infinities are handled according to IEEE standards.
    infinity_support: bool,

    // Used by SkSL to know when to generate polyfills.
    builtin_fma_support: bool,
    builtin_determinant_support: bool,

    // Used for specific driver bug work arounds
    can_use_void_in_sequence_expressions: bool,
    can_use_min_and_abs_together: bool,
    can_use_fract_for_negative_values: bool,
    must_force_negated_atan_param_to_float: bool,
    must_force_negated_ldexp_param_to_multiply: bool,
    // Returns whether a device incorrectly implements atan(y,x) as atan(y/x)
    atan2_implemented_as_atan_y_over_x: bool,
    // If this returns true some operation (could be a no op) must be called between floor and abs
    // to make sure the driver compiler doesn't inline them together which can cause a driver bug in
    // the shader.
    must_do_op_between_floor_and_abs: bool,
    // The D3D shader compiler, when targeting PS 3.0 (ie within ANGLE) fails to compile certain
    // constructs. See detailed comments in GrGLCaps.cpp.
    must_guard_division_even_after_explicit_zero_check: bool,
    // If false, SkSL uses a workaround so that sk_FragCoord doesn't actually query gl_FragCoord
    can_use_frag_coord: bool,
    // If true, then conditions in for loops need "&& true" to work around driver bugs.
    add_and_true_to_loop_condition: bool,
    // If true, then expressions such as "x && y" or "x || y" are rewritten as ternary to work
    // around driver bugs.
    unfold_short_circuit_as_ternary: bool,
    emulated_abs_int_function: bool,
    rewrite_do_while_loops: bool,
    rewrite_switch_statements: bool,
    remove_pow_with_constatnt_exponent: bool,
    // The Android emulator claims samplerExternalOES is an unknown type if a default precision
    // statement is made for the type.
    no_default_precision_for_external_samplers: bool,
    // ARM GPUs calculate `matrix * vector` in SPIR-V at full precision, even when the inputs are
    // RelaxedPrecision. Rewriting the multiply as a sum of vector*scalar fixes this. (skia:11769)
    rewrite_matrix_vector_mulitply: bool,
    // Rewrites matrix equality comparisons to avoid an Adreno driver bug. (skia:11308)
    rewrite_matrix_comparisons: bool,
    // Strips const from function parameters in the GLSL code generator. (skia:13858)
    remove_const_from_function_parameters: bool,
    // On some Android devices colors aren't accurate enough for the double lookup in the
    // Perlin noise shader. This workaround aggressively snaps colors to multiples of 1/255.
    perlin_noise_rounding_fix: bool,
    // Vulkan requires certain builtin variables be present, even if they're unused. At one time,
    // validation errors would result if sk_Clockwise was missing. Now, it's just (Adreno) driver
    // bugs that drop or corrupt draws if they're missing.
    must_declare_fragment_front_facing: bool,

    version_decl_string: String,

    shader_derivative_extension_string: Option<String>,
    external_texture_extension_string: Option<String>,
    second_external_texture_extension_string: Option<String>,
    fb_fetch_color_name: Option<String>,

    adv_blend_eq_interaction: AdvBlendEqInteraction,
}

impl Default for ShaderCaps {
    fn default() -> Self {
        Self {
            glsl_generation: GLSLGeneration::V330,

            dual_source_blending_support: false,
            shader_derivative_support: false,

            explicit_texture_lod_support: false,
            integer_support: false,
            non_square_matrix_support: false,
            inverse_hyperbolic_support: false,
            fb_fetch_support: false,
            fb_fetch_needs_custom_output: false,
            uses_precision_modifiers: false,
            flat_interpolation_support: false,
            no_perspective_interpolation_support: false,
            sample_mask_support: false,
            external_texture_support: false,
            float_is_32_bits: true,
            infinity_support: false,
            builtin_fma_support: true,
            builtin_determinant_support: true,

            can_use_void_in_sequence_expressions: true,
            can_use_min_and_abs_together: true,
            can_use_fract_for_negative_values: true,
            must_force_negated_atan_param_to_float: false,
            must_force_negated_ldexp_param_to_multiply: false,
            atan2_implemented_as_atan_y_over_x: false,
            must_do_op_between_floor_and_abs: false,
            must_guard_division_even_after_explicit_zero_check: false,
            can_use_frag_coord: true,
            add_and_true_to_loop_condition: false,
            unfold_short_circuit_as_ternary: false,
            emulated_abs_int_function: false,
            rewrite_do_while_loops: false,
            rewrite_switch_statements: false,
            remove_pow_with_constatnt_exponent: false,
            no_default_precision_for_external_samplers: false,
            rewrite_matrix_vector_mulitply: false,
            rewrite_matrix_comparisons: false,
            remove_const_from_function_parameters: false,
            perlin_noise_rounding_fix: false,
            must_declare_fragment_front_facing: false,

            version_decl_string: String::new(),

            shader_derivative_extension_string: None,
            external_texture_extension_string: None,
            second_external_texture_extension_string: None,
            fb_fetch_color_name: None,

            adv_blend_eq_interaction: AdvBlendEqInteraction::NotSupported,
        }
    }
}

impl ShaderCaps {
    #[must_use]
    #[inline]
    pub fn must_enable_adv_blend_eqs(&self) -> bool {
        self.adv_blend_eq_interaction >= AdvBlendEqInteraction::GeneralEnable
    }

    #[must_use]
    #[inline]
    pub fn must_declare_fragment_shader_output(&self) -> bool {
        self.glsl_generation > GLSLGeneration::V110
    }

    /// Returns the string of an extension that must be enabled in the shader to support derivatives.
    ///
    /// If nullptr is returned then no extension needs to be enabled. Before calling
    /// this function, the caller should check that shaderDerivativeSupport exists.
    #[must_use]
    #[inline]
    pub fn shader_derivative_extension_string(&self) -> Option<String> {
        debug_assert!(self.shader_derivative_support);
        self.shader_derivative_extension_string.clone()
    }

    // This returns the name of an extension that must be enabled in the shader to support external
    // textures. In some cases, two extensions must be enabled - the second extension is returned
    // by secondExternalTextureExtensionString(). If that function returns nullptr, then only one
    // extension is required.
    #[must_use]
    #[inline]
    pub fn external_texture_extension_string(&self) -> Option<String> {
        debug_assert!(self.external_texture_support);
        self.external_texture_extension_string.clone()
    }

    #[must_use]
    #[inline]
    pub fn second_external_texture_extension_string(&self) -> Option<String> {
        debug_assert!(self.external_texture_support);
        self.second_external_texture_extension_string.clone()
    }

    /// `SkSL` 300 requires support for derivatives, nonsquare matrices and bitwise integer operations.
    #[must_use]
    #[inline]
    pub fn supported_sksl_verion(&self) -> Version {
        if self.shader_derivative_support
            && self.non_square_matrix_support
            && self.integer_support
            && self.glsl_generation >= GLSLGeneration::V330
        {
            Version::V300
        } else {
            Version::V100
        }
    }

    #[must_use]
    #[inline]
    pub const fn supports_distance_field_text(&self) -> bool {
        self.shader_derivative_support
    }
}

// Various sets of caps for use in tests
pub struct ShaderCapsFactory {}

impl ShaderCapsFactory {
    #[must_use]
    pub fn make_default() -> ShaderCaps {
        ShaderCaps {
            version_decl_string: "#version 400".to_owned(),
            shader_derivative_support: true,
            ..Default::default()
        }
    }

    #[must_use]
    pub fn standalone() -> ShaderCaps {
        ShaderCaps {
            shader_derivative_support: true,
            explicit_texture_lod_support: true,
            flat_interpolation_support: true,
            no_perspective_interpolation_support: true,
            sample_mask_support: true,
            external_texture_support: true,
            ..Default::default()
        }
    }
}

//pub fn type_to_sksltype(_context: &Context, _type_: &Type) -> Option<SLType> {
/*
match type_ {
    context.types.void => Some(SkSLType::Void),
}
if (type.matches(*context.fTypes.fVoid    )) { *outType = SkSLType::kVoid;     return true; }
if (type.matches(*context.fTypes.fBool    )) { *outType = SkSLType::kBool;     return true; }
if (type.matches(*context.fTypes.fBool2   )) { *outType = SkSLType::kBool2;    return true; }
if (type.matches(*context.fTypes.fBool3   )) { *outType = SkSLType::kBool3;    return true; }
if (type.matches(*context.fTypes.fBool4   )) { *outType = SkSLType::kBool4;    return true; }
if (type.matches(*context.fTypes.fShort   )) { *outType = SkSLType::kShort;    return true; }
if (type.matches(*context.fTypes.fShort2  )) { *outType = SkSLType::kShort2;   return true; }
if (type.matches(*context.fTypes.fShort3  )) { *outType = SkSLType::kShort3;   return true; }
if (type.matches(*context.fTypes.fShort4  )) { *outType = SkSLType::kShort4;   return true; }
if (type.matches(*context.fTypes.fUShort  )) { *outType = SkSLType::kUShort;   return true; }
if (type.matches(*context.fTypes.fUShort2 )) { *outType = SkSLType::kUShort2;  return true; }
if (type.matches(*context.fTypes.fUShort3 )) { *outType = SkSLType::kUShort3;  return true; }
if (type.matches(*context.fTypes.fUShort4 )) { *outType = SkSLType::kUShort4;  return true; }
if (type.matches(*context.fTypes.fFloat   )) { *outType = SkSLType::kFloat;    return true; }
if (type.matches(*context.fTypes.fFloat2  )) { *outType = SkSLType::kFloat2;   return true; }
if (type.matches(*context.fTypes.fFloat3  )) { *outType = SkSLType::kFloat3;   return true; }
if (type.matches(*context.fTypes.fFloat4  )) { *outType = SkSLType::kFloat4;   return true; }
if (type.matches(*context.fTypes.fFloat2x2)) { *outType = SkSLType::kFloat2x2; return true; }
if (type.matches(*context.fTypes.fFloat3x3)) { *outType = SkSLType::kFloat3x3; return true; }
if (type.matches(*context.fTypes.fFloat4x4)) { *outType = SkSLType::kFloat4x4; return true; }
if (type.matches(*context.fTypes.fHalf    )) { *outType = SkSLType::kHalf;     return true; }
if (type.matches(*context.fTypes.fHalf2   )) { *outType = SkSLType::kHalf2;    return true; }
if (type.matches(*context.fTypes.fHalf3   )) { *outType = SkSLType::kHalf3;    return true; }
if (type.matches(*context.fTypes.fHalf4   )) { *outType = SkSLType::kHalf4;    return true; }
if (type.matches(*context.fTypes.fHalf2x2 )) { *outType = SkSLType::kHalf2x2;  return true; }
if (type.matches(*context.fTypes.fHalf3x3 )) { *outType = SkSLType::kHalf3x3;  return true; }
if (type.matches(*context.fTypes.fHalf4x4 )) { *outType = SkSLType::kHalf4x4;  return true; }
if (type.matches(*context.fTypes.fInt     )) { *outType = SkSLType::kInt;      return true; }
if (type.matches(*context.fTypes.fInt2    )) { *outType = SkSLType::kInt2;     return true; }
if (type.matches(*context.fTypes.fInt3    )) { *outType = SkSLType::kInt3;     return true; }
if (type.matches(*context.fTypes.fInt4    )) { *outType = SkSLType::kInt4;     return true; }
if (type.matches(*context.fTypes.fUInt    )) { *outType = SkSLType::kUInt;     return true; }
if (type.matches(*context.fTypes.fUInt2   )) { *outType = SkSLType::kUInt2;    return true; }
if (type.matches(*context.fTypes.fUInt3   )) { *outType = SkSLType::kUInt3;    return true; }
if (type.matches(*context.fTypes.fUInt4   )) { *outType = SkSLType::kUInt4;    return true; }
return false;
*/
//}