Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,114 @@
# SPDX-FileCopyrightText: 2024 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
set(INC_GLSL
.
..
../intern
# For variadic macros
../../blenlib
common
infos
)
set(SRC_GLSL_VERT
gpu_shader_2D_area_borders_vert.glsl
gpu_shader_2D_image_rect_vert.glsl
gpu_shader_2D_image_vert.glsl
gpu_shader_2D_node_socket_vert.glsl
gpu_shader_2D_point_uniform_size_aa_vert.glsl
gpu_shader_2D_point_uniform_size_outline_aa_vert.glsl
gpu_shader_2D_point_varying_size_varying_color_vert.glsl
gpu_shader_3D_polyline_vert.glsl
gpu_shader_2D_vert.glsl
gpu_shader_2D_widget_shadow_vert.glsl
gpu_shader_3D_clipped_uniform_color_vert.glsl
gpu_shader_3D_flat_color_vert.glsl
gpu_shader_3D_image_vert.glsl
gpu_shader_3D_line_dashed_uniform_color_vert.glsl
gpu_shader_3D_normal_vert.glsl
gpu_shader_3D_point_uniform_size_aa_vert.glsl
gpu_shader_3D_point_varying_size_varying_color_vert.glsl
gpu_shader_3D_point_flat_color_vert.glsl
gpu_shader_3D_smooth_color_vert.glsl
gpu_shader_display_fallback_vert.glsl
gpu_shader_gpencil_stroke_vert.glsl
gpu_shader_icon_multi_vert.glsl
gpu_shader_icon_vert.glsl
gpu_shader_keyframe_shape_vert.glsl
gpu_shader_sequencer_strips_vert.glsl
gpu_shader_sequencer_thumbs_vert.glsl
gpu_shader_text_vert.glsl
)
set(SRC_GLSL_FRAG
gpu_shader_2D_area_borders_frag.glsl
gpu_shader_2D_line_dashed_frag.glsl
gpu_shader_2D_node_socket_frag.glsl
gpu_shader_2D_widget_shadow_frag.glsl
gpu_shader_3D_polyline_frag.glsl
gpu_shader_3D_smooth_color_frag.glsl
gpu_shader_checker_frag.glsl
gpu_shader_depth_only_frag.glsl
gpu_shader_diag_stripes_frag.glsl
gpu_shader_display_fallback_frag.glsl
gpu_shader_flat_color_frag.glsl
gpu_shader_gpencil_stroke_frag.glsl
gpu_shader_icon_frag.glsl
gpu_shader_image_color_frag.glsl
gpu_shader_image_desaturate_frag.glsl
gpu_shader_image_frag.glsl
gpu_shader_image_overlays_merge_frag.glsl
gpu_shader_image_overlays_stereo_merge_frag.glsl
gpu_shader_image_shuffle_color_frag.glsl
gpu_shader_keyframe_shape_frag.glsl
gpu_shader_point_uniform_color_aa_frag.glsl
gpu_shader_point_uniform_color_outline_aa_frag.glsl
gpu_shader_point_varying_color_frag.glsl
gpu_shader_sequencer_scope_frag.glsl
gpu_shader_sequencer_strips_frag.glsl
gpu_shader_sequencer_thumbs_frag.glsl
gpu_shader_sequencer_zebra_frag.glsl
gpu_shader_simple_lighting_frag.glsl
gpu_shader_text_frag.glsl
gpu_shader_uniform_color_frag.glsl
)
set(SRC_GLSL_COMP
gpu_shader_sequencer_scope_comp.glsl
# TODO rename them properly to enable compilation.
# gpu_shader_index_2d_array_lines.glsl
# gpu_shader_index_2d_array_points.glsl
# gpu_shader_index_2d_array_tris.glsl
)
set(SRC_VULKAN_GLSL_COMP
# TODO incorrect path, if corrected fails c++ compile
# vk_backbuffer_blit_comp.glsl
)
set(SRC_BSL
bsl_shader_linting.cc
)
if(WITH_VULKAN_BACKEND)
list(APPEND SRC_GLSL_COMP ${SRC_VULKAN_GLSL_COMP})
endif()
set(SRC_GLSL_LIB
common/gpu_shader_print_lib.glsl
)
# Compile shaders with shader code.
if(WITH_GPU_SHADER_CPP_COMPILATION)
compile_sources_as_cpp(gpu_cpp_shaders_vert "${SRC_GLSL_VERT}" "GPU_VERTEX_SHADER")
compile_sources_as_cpp(gpu_cpp_shaders_frag "${SRC_GLSL_FRAG}" "GPU_FRAGMENT_SHADER")
compile_sources_as_cpp(gpu_cpp_shaders_comp "${SRC_GLSL_COMP}" "GPU_COMPUTE_SHADER")
compile_sources_as_cpp(gpu_cpp_shaders_bsl "${SRC_GLSL_COMP}" "")
# Only enable to make sure they compile on their own.
# Otherwise it creates a warning about `pragma once`.
# compile_sources_as_cpp(gpu_cpp_shaders_lib "${SRC_GLSL_LIB}" "GPU_LIBRARY_SHADER")
endif()

View File

@@ -0,0 +1,13 @@
/* SPDX-FileCopyrightText: 2026 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Compile shader files as C++ inside one compilation unit to lint syntax and get IDE integration.
*/
#include "gpu_shader_2D_nodelink.bsl.hh" /* IWYU pragma: export */
#include "gpu_shader_2D_update_mipmaps.bsl.hh" /* IWYU pragma: export */
#include "gpu_shader_2D_widget_base.bsl.hh" /* IWYU pragma: export */
void main() {}

View File

@@ -0,0 +1,96 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
/**
* Library to read packed vertex buffer data of a `gpu::Batch` using a SSBO rather than using input
* assembly. It is **not** needed to use these macros if the data is known to be aligned and
* contiguous. Arrays of any 4-byte component vector except 3 component vectors do not need this.
*
* Implemented as macros to avoid compiler differences with buffer qualifiers.
*/
/** Returns index in the first component. Needed for non trivially packed data. */
uint gpu_attr_load_index(uint vertex_index, int2 stride_and_offset)
{
return vertex_index * uint(stride_and_offset.x) + uint(stride_and_offset.y);
}
float4 gpu_attr_decode_1010102_snorm(uint in_data)
{
/* TODO(fclem): Improve this. */
uint4 v_data = uint4(in_data) >> uint4(0, 10, 20, 30);
bool4 v_sign = greaterThan(v_data & uint4(0x3FF, 0x3FF, 0x3FF, 0x3),
uint4(0x1FF, 0x1FF, 0x1FF, 0x1));
uint4 v_data_u = floatBitsToUint(mix(uintBitsToFloat(v_data), uintBitsToFloat(~v_data), v_sign));
float4 mag = float4(v_data_u & uint4(0x1FF, 0x1FF, 0x1FF, 0x1)) /
float4(0x1FF, 0x1FF, 0x1FF, 0x1);
return mix(mag, -mag, v_sign);
}
float4 gpu_attr_decode_short4_to_float4_snorm(uint data0, uint data1)
{
/* TODO(fclem): Improve this. */
uint4 v_data = uint4(data0, data0 >> 16u, data1, data1 >> 16u);
bool4 v_sign = greaterThan(v_data & uint4(0xFFFF), uint4(0x7FFF));
uint4 v_data_u = floatBitsToUint(mix(uintBitsToFloat(v_data), uintBitsToFloat(~v_data), v_sign));
float4 mag = float4(v_data_u & 0x7FFFu) / float(0x7FFF);
return mix(mag, -mag, v_sign);
}
uint4 gpu_attr_decode_uchar4_to_uint4(uint in_data)
{
return (uint4(in_data) >> uint4(0, 8, 16, 24)) & uint4(0xFF);
}
/* TODO(fclem): Once the stride and offset are made obsolete, we can think of wrapping vec3 into
* structs of floats as they do not have the 16byte alignment restriction. */
#define gpu_attr_load_triplet(_type, _data, _stride_and_offset, _i) \
_type(_data[gpu_attr_load_index(_i, _stride_and_offset) + 0], \
_data[gpu_attr_load_index(_i, _stride_and_offset) + 1], \
_data[gpu_attr_load_index(_i, _stride_and_offset) + 2])
#define gpu_attr_load_tuple(_type, _data, _stride_and_offset, _i) \
_type(_data[gpu_attr_load_index(_i, _stride_and_offset) + 0], \
_data[gpu_attr_load_index(_i, _stride_and_offset) + 1])
/* Assumes _data is declared as an array of float. */
#define gpu_attr_load_float3(_data, _stride_and_offset, _i) \
gpu_attr_load_triplet(float3, _data, _stride_and_offset, _i)
#define gpu_attr_load_float2(_data, _stride_and_offset, _i) \
gpu_attr_load_tuple(float2, _data, _stride_and_offset, _i)
/* Assumes _data is declared as an array of uint. */
#define gpu_attr_load_uint3(_data, _stride_and_offset, _i) \
gpu_attr_load_triplet(int3, _data, _stride_and_offset, _i)
#define gpu_attr_load_uint2(_data, _stride_and_offset, _i) \
gpu_attr_load_tuple(int2, _data, _stride_and_offset, _i)
/* Assumes _data is declared as an array of int. */
#define gpu_attr_load_int3(_data, _stride_and_offset, _i) \
gpu_attr_load_triplet(uint3, _data, _stride_and_offset, _i)
#define gpu_attr_load_int2(_data, _stride_and_offset, _i) \
gpu_attr_load_tuple(uint2, _data, _stride_and_offset, _i)
/* Assumes _data is declared as an array of uint. */
#define gpu_attr_load_uint_1010102_snorm(_data, _stride_and_offset, _i) \
gpu_attr_decode_1010102_snorm(_data[gpu_attr_load_index(_i, _stride_and_offset)])
/* TODO(fclem): Once the stride and offset are made obsolete, we can think of wrapping short4 into
* structs of uint as they do not have the 16byte alignment restriction. */
/* Assumes _data is declared as an array of uint. */
#define gpu_attr_load_short4_snorm(_data, _stride_and_offset, _i) \
gpu_attr_decode_short4_to_float4_snorm(_data[gpu_attr_load_index(_i, _stride_and_offset) + 0], \
_data[gpu_attr_load_index(_i, _stride_and_offset) + 1])
/* Assumes _data is declared as an array of uint. */
#define gpu_attr_load_uchar4(_data, _stride_and_offset, _i) \
gpu_attr_decode_uchar4_to_uint4(_data[gpu_attr_load_index(_i, _stride_and_offset)])
/* Assumes _data is declared as an array of uint. */
#define gpu_attr_load_uchar(_data, _i) \
gpu_attr_decode_uchar4_to_uint4( \
_data[gpu_attr_load_index(uint(_i) >> 2u, int2(1, 0))])[uint(_i) & 3u]
/* Assumes _data is declared as an array of uint. */
#define gpu_attr_load_bool(_data, _i) (gpu_attr_load_uchar(_data, _i) != 0u)

View File

@@ -0,0 +1,77 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
/** \param f: Offset from texel center in pixel space. */
void cubic_bspline_coefficients(float2 f, float2 &w0, float2 &w1, float2 &w2, float2 &w3)
{
float2 f2 = f * f;
float2 f3 = f2 * f;
/* Optimized formulae for cubic B-Spline coefficients. */
w3 = f3 / 6.0f;
w0 = -w3 + f2 * 0.5f - f * 0.5f + 1.0f / 6.0f;
w1 = f3 * 0.5f - f2 * 1.0f + 2.0f / 3.0f;
w2 = 1.0f - w0 - w1 - w3;
}
/* Samples the given 2D sampler at the given coordinates using Bicubic interpolation. This function
* uses an optimized algorithm which assumes a linearly filtered sampler, so the caller needs to
* take that into account when setting up the sampler. */
float4 texture_bicubic(sampler2D sampler_2d, float2 coordinates)
{
float2 texture_size = float2(textureSize(sampler_2d, 0).xy);
coordinates.xy *= texture_size;
float2 w0, w1, w2, w3;
float2 texel_center = floor(coordinates.xy - 0.5f) + 0.5f;
cubic_bspline_coefficients(coordinates.xy - texel_center, w0, w1, w2, w3);
#if 1 /* Optimized version using 4 filtered taps. */
float2 s0 = w0 + w1;
float2 s1 = w2 + w3;
float2 f0 = w1 / (w0 + w1);
float2 f1 = w3 / (w2 + w3);
float4 sampling_coordinates;
sampling_coordinates.xy = texel_center - 1.0f + f0;
sampling_coordinates.zw = texel_center + 1.0f + f1;
sampling_coordinates /= texture_size.xyxy;
float4 sampled_color = textureLod(sampler_2d, sampling_coordinates.xy, 0.0f) * s0.x * s0.y;
sampled_color += textureLod(sampler_2d, sampling_coordinates.zy, 0.0f) * s1.x * s0.y;
sampled_color += textureLod(sampler_2d, sampling_coordinates.xw, 0.0f) * s0.x * s1.y;
sampled_color += textureLod(sampler_2d, sampling_coordinates.zw, 0.0f) * s1.x * s1.y;
return sampled_color;
#else /* Reference brute-force 16 taps. */
float4 color = texelFetch(sampler_2d, int2(texel_center + float2(-1.0f, -1.0f)), 0) * w0.x *
w0.y;
color += texelFetch(sampler_2d, int2(texel_center + float2(0.0f, -1.0f)), 0) * w1.x * w0.y;
color += texelFetch(sampler_2d, int2(texel_center + float2(1.0f, -1.0f)), 0) * w2.x * w0.y;
color += texelFetch(sampler_2d, int2(texel_center + float2(2.0f, -1.0f)), 0) * w3.x * w0.y;
color += texelFetch(sampler_2d, int2(texel_center + float2(-1.0f, 0.0f)), 0) * w0.x * w1.y;
color += texelFetch(sampler_2d, int2(texel_center + float2(0.0f, 0.0f)), 0) * w1.x * w1.y;
color += texelFetch(sampler_2d, int2(texel_center + float2(1.0f, 0.0f)), 0) * w2.x * w1.y;
color += texelFetch(sampler_2d, int2(texel_center + float2(2.0f, 0.0f)), 0) * w3.x * w1.y;
color += texelFetch(sampler_2d, int2(texel_center + float2(-1.0f, 1.0f)), 0) * w0.x * w2.y;
color += texelFetch(sampler_2d, int2(texel_center + float2(0.0f, 1.0f)), 0) * w1.x * w2.y;
color += texelFetch(sampler_2d, int2(texel_center + float2(1.0f, 1.0f)), 0) * w2.x * w2.y;
color += texelFetch(sampler_2d, int2(texel_center + float2(2.0f, 1.0f)), 0) * w3.x * w2.y;
color += texelFetch(sampler_2d, int2(texel_center + float2(-1.0f, 2.0f)), 0) * w0.x * w3.y;
color += texelFetch(sampler_2d, int2(texel_center + float2(0.0f, 2.0f)), 0) * w1.x * w3.y;
color += texelFetch(sampler_2d, int2(texel_center + float2(1.0f, 2.0f)), 0) * w2.x * w3.y;
color += texelFetch(sampler_2d, int2(texel_center + float2(2.0f, 2.0f)), 0) * w3.x * w3.y;
return color;
#endif
}

View File

@@ -0,0 +1,63 @@
/* SPDX-FileCopyrightText: 2019-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
[[node]]
void valtorgb_opti_constant(
float fac, float edge, float4 color1, float4 color2, float4 &outcol, float &outalpha)
{
outcol = (fac > edge) ? color2 : color1;
outalpha = outcol.a;
}
[[node]]
void valtorgb_opti_linear(
float fac, float2 mulbias, float4 color1, float4 color2, float4 &outcol, float &outalpha)
{
fac = clamp(fac * mulbias.x + mulbias.y, 0.0f, 1.0f);
outcol = mix(color1, color2, fac);
outalpha = outcol.a;
}
[[node]]
void valtorgb_opti_ease(
float fac, float2 mulbias, float4 color1, float4 color2, float4 &outcol, float &outalpha)
{
fac = clamp(fac * mulbias.x + mulbias.y, 0.0f, 1.0f);
fac = fac * fac * (3.0f - 2.0f * fac);
outcol = mix(color1, color2, fac);
outalpha = outcol.a;
}
/* Color maps are stored in texture samplers, so ensure that the coordinate evaluates the sampler
* at the center of the pixels, because samplers are evaluated using linear interpolation. Given
* the coordinate in the [0, 1] range. */
float compute_color_map_coordinate(float coordinate)
{
/* Color maps have a fixed width of 257. We offset by the equivalent of half a pixel and scale
* down such that the normalized coordinate 1.0 corresponds to the center of the last pixel. */
constexpr float sampler_resolution = 257.0f;
constexpr float sampler_offset = 0.5f / sampler_resolution;
constexpr float sampler_scale = 1.0f - (1.0f / sampler_resolution);
return coordinate * sampler_scale + sampler_offset;
}
[[node]]
void valtorgb(float fac, sampler1DArray colormap, float layer, float4 &outcol, float &outalpha)
{
outcol = texture(colormap, float2(compute_color_map_coordinate(fac), layer));
outalpha = outcol.a;
}
[[node]]
void valtorgb_nearest(
float fac, sampler1DArray colormap, float layer, float4 &outcol, float &outalpha)
{
fac = clamp(fac, 0.0f, 1.0f);
outcol = texelFetch(colormap, int2(fac * (textureSize(colormap, 0).x - 1), layer), 0);
outalpha = outcol.a;
}

View File

@@ -0,0 +1,307 @@
/* SPDX-FileCopyrightText: 2019-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
[[node]]
void rgb_to_hsv(float4 rgb, float4 &outcol)
{
float cmax, cmin, h, s, v, cdelta;
float3 c;
cmax = max(rgb[0], max(rgb[1], rgb[2]));
cmin = min(rgb[0], min(rgb[1], rgb[2]));
cdelta = cmax - cmin;
v = cmax;
if (cmax != 0.0f) {
s = cdelta / cmax;
}
else {
s = 0.0f;
h = 0.0f;
}
if (s == 0.0f) {
h = 0.0f;
}
else {
c = (float3(cmax) - rgb.xyz) / cdelta;
if (rgb.x == cmax) {
h = c[2] - c[1];
}
else if (rgb.y == cmax) {
h = 2.0f + c[0] - c[2];
}
else {
h = 4.0f + c[1] - c[0];
}
h /= 6.0f;
if (h < 0.0f) {
h += 1.0f;
}
}
outcol = float4(h, s, v, rgb.w);
}
[[node]]
void hsv_to_rgb(float4 hsv, float4 &outcol)
{
float i, f, p, q, t, h, s, v;
float3 rgb;
h = hsv[0];
s = hsv[1];
v = hsv[2];
if (s == 0.0f) {
rgb = float3(v, v, v);
}
else {
if (h == 1.0f) {
h = 0.0f;
}
h *= 6.0f;
i = floor(h);
f = h - i;
rgb = float3(f, f, f);
p = v * (1.0f - s);
q = v * (1.0f - (s * f));
t = v * (1.0f - (s * (1.0f - f)));
if (i == 0.0f) {
rgb = float3(v, t, p);
}
else if (i == 1.0f) {
rgb = float3(q, v, p);
}
else if (i == 2.0f) {
rgb = float3(p, v, t);
}
else if (i == 3.0f) {
rgb = float3(p, q, v);
}
else if (i == 4.0f) {
rgb = float3(t, p, v);
}
else {
rgb = float3(v, p, q);
}
}
outcol = float4(rgb, hsv.w);
}
[[node]]
void rgb_to_hsl(float4 rgb, float4 &outcol)
{
float cmax, cmin, h, s, l;
cmax = max(rgb[0], max(rgb[1], rgb[2]));
cmin = min(rgb[0], min(rgb[1], rgb[2]));
l = min(1.0f, (cmax + cmin) / 2.0f);
if (cmax == cmin) {
h = s = 0.0f; /* achromatic */
}
else {
float cdelta = cmax - cmin;
s = l > 0.5f ? cdelta / (2.0f - cmax - cmin) : cdelta / (cmax + cmin);
if (cmax == rgb[0]) {
h = (rgb[1] - rgb[2]) / cdelta + (rgb[1] < rgb[2] ? 6.0f : 0.0f);
}
else if (cmax == rgb[1]) {
h = (rgb[2] - rgb[0]) / cdelta + 2.0f;
}
else {
h = (rgb[0] - rgb[1]) / cdelta + 4.0f;
}
}
h /= 6.0f;
outcol = float4(h, s, l, rgb.w);
}
[[node]]
void hsl_to_rgb(float4 hsl, float4 &outcol)
{
float nr, ng, nb, chroma, h, s, l;
h = hsl[0];
s = hsl[1];
l = hsl[2];
nr = abs(h * 6.0f - 3.0f) - 1.0f;
ng = 2.0f - abs(h * 6.0f - 2.0f);
nb = 2.0f - abs(h * 6.0f - 4.0f);
nr = clamp(nr, 0.0f, 1.0f);
nb = clamp(nb, 0.0f, 1.0f);
ng = clamp(ng, 0.0f, 1.0f);
chroma = (1.0f - abs(2.0f * l - 1.0f)) * s;
outcol = float4(
(nr - 0.5f) * chroma + l, (ng - 0.5f) * chroma + l, (nb - 0.5f) * chroma + l, hsl.w);
}
/* ** YCCA to RGBA ** */
[[node]]
void ycca_to_rgba_itu_601(float4 ycca, float4 &color)
{
ycca.xyz *= 255.0f;
ycca.xyz -= float3(16.0f, 128.0f, 128.0f);
color.rgb = float3x3(1.164f, 1.164f, 1.164f, 0.0f, -0.392f, 2.017f, 1.596f, -0.813f, 0.0f) *
ycca.xyz;
color.rgb /= 255.0f;
color.a = ycca.a;
}
[[node]]
void ycca_to_rgba_itu_709(float4 ycca, float4 &color)
{
ycca.xyz *= 255.0f;
ycca.xyz -= float3(16.0f, 128.0f, 128.0f);
color.rgb = float3x3(1.164f, 1.164f, 1.164f, 0.0f, -0.213f, 2.115f, 1.793f, -0.534f, 0.0f) *
ycca.xyz;
color.rgb /= 255.0f;
color.a = ycca.a;
}
[[node]]
void ycca_to_rgba_jpeg(float4 ycca, float4 &color)
{
ycca.xyz *= 255.0f;
color.rgb = float3x3(1.0f, 1.0f, 1.0f, 0.0f, -0.34414f, 1.772f, 1.402f, -0.71414f, 0.0f) *
ycca.xyz;
color.rgb += float3(-179.456f, 135.45984f, -226.816f);
color.rgb /= 255.0f;
color.a = ycca.a;
}
/* ** RGBA to YCCA ** */
[[node]]
void rgba_to_ycca_itu_601(float4 rgba, float4 &ycca)
{
rgba.rgb *= 255.0f;
ycca.xyz = float3x3(0.257f, -0.148f, 0.439f, 0.504f, -0.291f, -0.368f, 0.098f, 0.439f, -0.071f) *
rgba.rgb;
ycca.xyz += float3(16.0f, 128.0f, 128.0f);
ycca.xyz /= 255.0f;
ycca.a = rgba.a;
}
[[node]]
void rgba_to_ycca_itu_709(float4 rgba, float4 &ycca)
{
rgba.rgb *= 255.0f;
ycca.xyz = float3x3(0.183f, -0.101f, 0.439f, 0.614f, -0.338f, -0.399f, 0.062f, 0.439f, -0.040f) *
rgba.rgb;
ycca.xyz += float3(16.0f, 128.0f, 128.0f);
ycca.xyz /= 255.0f;
ycca.a = rgba.a;
}
[[node]]
void rgba_to_ycca_jpeg(float4 rgba, float4 &ycca)
{
rgba.rgb *= 255.0f;
ycca.xyz = float3x3(
0.299f, -0.16874f, 0.5f, 0.587f, -0.33126f, -0.41869f, 0.114f, 0.5f, -0.08131f) *
rgba.rgb;
ycca.xyz += float3(0.0f, 128.0f, 128.0f);
ycca.xyz /= 255.0f;
ycca.a = rgba.a;
}
/* ** YUVA to RGBA ** */
[[node]]
void yuva_to_rgba_itu_709(float4 yuva, float4 &color)
{
color.rgb = float3x3(1.0f, 1.0f, 1.0f, 0.0f, -0.21482f, 2.12798f, 1.28033f, -0.38059f, 0.0f) *
yuva.xyz;
color.a = yuva.a;
}
/* ** RGBA to YUVA ** */
[[node]]
void rgba_to_yuva_itu_709(float4 rgba, float4 &yuva)
{
yuva.xyz =
float3x3(
0.2126f, -0.09991f, 0.615f, 0.7152f, -0.33609f, -0.55861f, 0.0722f, 0.436f, -0.05639f) *
rgba.rgb;
yuva.a = rgba.a;
}
/* ** Alpha Handling ** */
[[node]]
void color_alpha_clear(float4 color, float4 &result)
{
result = float4(color.rgb, 1.0f);
}
[[node]]
void color_alpha_premultiply(float4 color, float4 &result)
{
result = float4(color.rgb * color.a, color.a);
}
[[node]]
void color_alpha_unpremultiply(float4 color, float4 &result)
{
if (color.a == 0.0f || color.a == 1.0f) {
result = color;
}
else {
result = float4(color.rgb / color.a, color.a);
}
}
float linear_rgb_to_srgb(float color)
{
if (color < 0.0031308f) {
return (color < 0.0f) ? 0.0f : color * 12.92f;
}
return 1.055f * pow(color, 1.0f / 2.4f) - 0.055f;
}
float3 linear_rgb_to_srgb(float3 color)
{
return float3(
linear_rgb_to_srgb(color.r), linear_rgb_to_srgb(color.g), linear_rgb_to_srgb(color.b));
}
float srgb_to_linear_rgb(float color)
{
if (color < 0.04045f) {
return (color < 0.0f) ? 0.0f : color * (1.0f / 12.92f);
}
return pow((color + 0.055f) * (1.0f / 1.055f), 2.4f);
}
float3 srgb_to_linear_rgb(float3 color)
{
return float3(
srgb_to_linear_rgb(color.r), srgb_to_linear_rgb(color.g), srgb_to_linear_rgb(color.b));
}
float get_luminance(float3 color, float3 luminance_coefficients)
{
return dot(color, luminance_coefficients);
}

View File

@@ -0,0 +1,362 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
float4 white_balance(float4 color, float4 black_level, float4 white_level)
{
float4 range = max(white_level - black_level, float4(1e-5f));
return (color - black_level) / range;
}
float extrapolate_if_needed(float parameter, float value, float start_slope, float end_slope)
{
if (parameter < 0.0f) {
return value + parameter * start_slope;
}
if (parameter > 1.0f) {
return value + (parameter - 1.0f) * end_slope;
}
return value;
}
/* Same as extrapolate_if_needed but vectorized. */
float3 extrapolate_if_needed(float3 parameters,
float3 values,
float3 start_slopes,
float3 end_slopes)
{
float3 end_or_zero_slopes = mix(float3(0.0f), end_slopes, greaterThan(parameters, float3(1.0f)));
float3 slopes = mix(end_or_zero_slopes, start_slopes, lessThan(parameters, float3(0.0f)));
parameters = parameters - mix(float3(0.0f), float3(1.0f), greaterThan(parameters, float3(1.0f)));
return values + parameters * slopes;
}
/* Curve maps are stored in texture samplers, so ensure that the parameters evaluate the sampler at
* the center of the pixels, because samplers are evaluated using linear interpolation. Given the
* parameter in the [0, 1] range. */
float compute_curve_map_coordinates(float parameter)
{
/* Curve maps have a fixed width of 257. We offset by the equivalent of half a pixel and scale
* down such that the normalized parameter 1.0 corresponds to the center of the last pixel. */
float sampler_offset = 0.5f / 257.0f;
float sampler_scale = 1.0f - (1.0f / 257.0f);
return parameter * sampler_scale + sampler_offset;
}
/* Same as compute_curve_map_coordinates but vectorized. */
float3 compute_curve_map_coordinates(float3 parameters)
{
float sampler_offset = 0.5f / 257.0f;
float sampler_scale = 1.0f - (1.0f / 257.0f);
return parameters * sampler_scale + sampler_offset;
}
[[node]]
void curves_combined_rgb(float factor,
float4 color,
float4 black_level,
float4 white_level,
sampler1DArray curve_map,
const float layer,
float4 range_minimums,
float4 range_dividers,
float4 start_slopes,
float4 end_slopes,
float4 &result)
{
float4 balanced = white_balance(color, black_level, white_level);
/* First, evaluate alpha curve map at all channels. The alpha curve is the Combined curve in the
* UI. The channels are first normalized into the [0, 1] range. */
float3 parameters = (balanced.rgb - range_minimums.aaa) * range_dividers.aaa;
float3 coordinates = compute_curve_map_coordinates(parameters);
result.r = texture(curve_map, float2(coordinates.x, layer)).a;
result.g = texture(curve_map, float2(coordinates.y, layer)).a;
result.b = texture(curve_map, float2(coordinates.z, layer)).a;
/* Then, extrapolate if needed. */
result.rgb = extrapolate_if_needed(parameters, result.rgb, start_slopes.aaa, end_slopes.aaa);
/* Then, evaluate each channel on its curve map. The channels are first normalized into the
* [0, 1] range. */
parameters = (result.rgb - range_minimums.rgb) * range_dividers.rgb;
coordinates = compute_curve_map_coordinates(parameters);
result.r = texture(curve_map, float2(coordinates.r, layer)).r;
result.g = texture(curve_map, float2(coordinates.g, layer)).g;
result.b = texture(curve_map, float2(coordinates.b, layer)).b;
/* Then, extrapolate again if needed. */
result.rgb = extrapolate_if_needed(parameters, result.rgb, start_slopes.rgb, end_slopes.rgb);
result.a = color.a;
result = mix(color, result, factor);
}
[[node]]
void curves_combined_rgb_compositor(float4 color,
float factor,
float4 black_level,
float4 white_level,
sampler1DArray curve_map,
const float layer,
float4 range_minimums,
float4 range_dividers,
float4 start_slopes,
float4 end_slopes,
float4 &result)
{
curves_combined_rgb(factor,
color,
black_level,
white_level,
curve_map,
layer,
range_minimums,
range_dividers,
start_slopes,
end_slopes,
result);
}
[[node]]
void curves_combined_only(float factor,
float4 color,
float4 black_level,
float4 white_level,
sampler1DArray curve_map,
const float layer,
float range_minimum,
float range_divider,
float start_slope,
float end_slope,
float4 &result)
{
float4 balanced = white_balance(color, black_level, white_level);
/* Evaluate alpha curve map at all channels. The alpha curve is the Combined curve in the
* UI. The channels are first normalized into the [0, 1] range. */
float3 parameters = (balanced.rgb - float3(range_minimum)) * float3(range_divider);
float3 coordinates = compute_curve_map_coordinates(parameters);
result.r = texture(curve_map, float2(coordinates.x, layer)).a;
result.g = texture(curve_map, float2(coordinates.y, layer)).a;
result.b = texture(curve_map, float2(coordinates.z, layer)).a;
/* Then, extrapolate if needed. */
result.rgb = extrapolate_if_needed(
parameters, result.rgb, float3(start_slope), float3(end_slope));
result.a = color.a;
result = mix(color, result, factor);
}
[[node]]
void curves_combined_only_compositor(float4 color,
float factor,
float4 black_level,
float4 white_level,
sampler1DArray curve_map,
const float layer,
float range_minimum,
float range_divider,
float start_slope,
float end_slope,
float4 &result)
{
curves_combined_only(factor,
color,
black_level,
white_level,
curve_map,
layer,
range_minimum,
range_divider,
start_slope,
end_slope,
result);
}
/* Contrary to standard tone curve implementations, the film-like implementation tries to preserve
* the hue of the colors as much as possible. To understand why this might be a problem, consider
* the violet color (0.5, 0.0, 1.0). If this color was to be evaluated at a power curve x^4, the
* color will be blue (0.0625, 0.0, 1.0). So the color changes and not just its luminosity,
* which is what film-like tone curves tries to avoid.
*
* First, the channels with the lowest and highest values are identified and evaluated at the
* curve. Then, the third channel---the median---is computed while maintaining the original hue of
* the color. To do that, we look at the equation for deriving the hue from RGB values. Assuming
* the maximum, minimum, and median channels are known, and ignoring the 1/3 period offset of the
* hue, the equation is:
*
* hue = (median - min) / (max - min) [1]
*
* Since we have the new values for the minimum and maximum after evaluating at the curve, we also
* have:
*
* hue = (new_median - new_min) / (new_max - new_min) [2]
*
* Since we want the hue to be equivalent, by equating [1] and [2] and rearranging:
*
* (new_median - new_min) / (new_max - new_min) = (median - min) / (max - min)
* new_median - new_min = (new_max - new_min) * (median - min) / (max - min)
* new_median = new_min + (new_max - new_min) * (median - min) / (max - min)
* new_median = new_min + (median - min) * ((new_max - new_min) / (max - min)) [QED]
*
* Which gives us the median color that preserves the hue. More intuitively, the median is computed
* such that the change in the distance from the median to the minimum is proportional to the
* change in the distance from the minimum to the maximum. Finally, each of the new minimum,
* maximum, and median values are written to the color channel that they were originally extracted
* from. */
[[node]]
void curves_film_like(float factor,
float4 color,
float4 black_level,
float4 white_level,
sampler1DArray curve_map,
const float layer,
float range_minimum,
float range_divider,
float start_slope,
float end_slope,
float4 &result)
{
float4 balanced = white_balance(color, black_level, white_level);
/* Find the maximum, minimum, and median of the color channels. */
float minimum = min(balanced.r, min(balanced.g, balanced.b));
float maximum = max(balanced.r, max(balanced.g, balanced.b));
float median = max(min(balanced.r, balanced.g), min(balanced.b, max(balanced.r, balanced.g)));
/* Evaluate alpha curve map at the maximum and minimum channels. The alpha curve is the Combined
* curve in the UI. The channels are first normalized into the [0, 1] range. */
float min_parameter = (minimum - range_minimum) * range_divider;
float max_parameter = (maximum - range_minimum) * range_divider;
float min_coordinates = compute_curve_map_coordinates(min_parameter);
float max_coordinates = compute_curve_map_coordinates(max_parameter);
float new_min = texture(curve_map, float2(min_coordinates, layer)).a;
float new_max = texture(curve_map, float2(max_coordinates, layer)).a;
/* Then, extrapolate if needed. */
new_min = extrapolate_if_needed(min_parameter, new_min, start_slope, end_slope);
new_max = extrapolate_if_needed(max_parameter, new_max, start_slope, end_slope);
/* Compute the new median using the ratio between the new and the original range. */
float scaling_ratio = (new_max - new_min) / (maximum - minimum);
float new_median = new_min + (median - minimum) * scaling_ratio;
/* Write each value to its original channel. */
bool3 channel_is_min = equal(balanced.rgb, float3(minimum));
float3 median_or_min = mix(float3(new_median), float3(new_min), channel_is_min);
bool3 channel_is_max = equal(balanced.rgb, float3(maximum));
result.rgb = mix(median_or_min, float3(new_max), channel_is_max);
result.a = color.a;
result = mix(color, result, clamp(factor, 0.0f, 1.0f));
}
[[node]]
void curves_film_like_compositor(float4 color,
float factor,
float4 black_level,
float4 white_level,
sampler1DArray curve_map,
const float layer,
float range_minimum,
float range_divider,
float start_slope,
float end_slope,
float4 &result)
{
curves_film_like(factor,
color,
black_level,
white_level,
curve_map,
layer,
range_minimum,
range_divider,
start_slope,
end_slope,
result);
}
[[node]]
void curves_vector(float3 vector,
sampler1DArray curve_map,
const float layer,
float3 range_minimums,
float3 range_dividers,
float3 start_slopes,
float3 end_slopes,
float3 &result)
{
/* Evaluate each component on its curve map.
* The components are first normalized into the [0, 1] range. */
float3 parameters = (vector - range_minimums) * range_dividers;
float3 coordinates = compute_curve_map_coordinates(parameters);
result.x = texture(curve_map, float2(coordinates.x, layer)).x;
result.y = texture(curve_map, float2(coordinates.y, layer)).y;
result.z = texture(curve_map, float2(coordinates.z, layer)).z;
/* Then, extrapolate if needed. */
result = extrapolate_if_needed(parameters, result, start_slopes, end_slopes);
}
[[node]]
void curves_vector_mixed(float factor,
float3 vector,
sampler1DArray curve_map,
const float layer,
float3 range_minimums,
float3 range_dividers,
float3 start_slopes,
float3 end_slopes,
float3 &result)
{
curves_vector(
vector, curve_map, layer, range_minimums, range_dividers, start_slopes, end_slopes, result);
result = mix(vector, result, factor);
}
[[node]]
void curves_float(float value,
sampler1DArray curve_map,
const float layer,
float range_minimum,
float range_divider,
float start_slope,
float end_slope,
float &result)
{
/* Evaluate the normalized value on the first curve map. */
float parameter = (value - range_minimum) * range_divider;
float coordinates = compute_curve_map_coordinates(parameter);
result = texture(curve_map, float2(coordinates, layer)).x;
/* Then, extrapolate if needed. */
result = extrapolate_if_needed(parameter, result, start_slope, end_slope);
}
[[node]]
void curves_float_mixed(float factor,
float value,
sampler1DArray curve_map,
const float layer,
float range_minimum,
float range_divider,
float start_slope,
float end_slope,
float &result)
{
curves_float(
value, curve_map, layer, range_minimum, range_divider, start_slope, end_slope, result);
result = mix(value, result, factor);
}

View File

@@ -0,0 +1,335 @@
/* SPDX-FileCopyrightText: 2019-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
/* ***** Jenkins Lookup3 Hash Functions ***** */
/* Source: http://burtleburtle.net/bob/c/lookup3.c */
#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
#define mix(a, b, c) \
{ \
a -= c; \
a ^= rot(c, 4); \
c += b; \
b -= a; \
b ^= rot(a, 6); \
a += c; \
c -= b; \
c ^= rot(b, 8); \
b += a; \
a -= c; \
a ^= rot(c, 16); \
c += b; \
b -= a; \
b ^= rot(a, 19); \
a += c; \
c -= b; \
c ^= rot(b, 4); \
b += a; \
}
#define final(a, b, c) \
{ \
c ^= b; \
c -= rot(b, 14); \
a ^= c; \
a -= rot(c, 11); \
b ^= a; \
b -= rot(a, 25); \
c ^= b; \
c -= rot(b, 16); \
a ^= c; \
a -= rot(c, 4); \
b ^= a; \
b -= rot(a, 14); \
c ^= b; \
c -= rot(b, 24); \
}
uint hash_uint(uint kx)
{
uint a, b, c;
a = b = c = 0xdeadbeefu + (1u << 2u) + 13u;
a += kx;
final(a, b, c);
return c;
}
uint hash_uint2(uint kx, uint ky)
{
uint a, b, c;
a = b = c = 0xdeadbeefu + (2u << 2u) + 13u;
b += ky;
a += kx;
final(a, b, c);
return c;
}
uint hash_uint3(uint kx, uint ky, uint kz)
{
uint a, b, c;
a = b = c = 0xdeadbeefu + (3u << 2u) + 13u;
c += kz;
b += ky;
a += kx;
final(a, b, c);
return c;
}
uint hash_uint4(uint kx, uint ky, uint kz, uint kw)
{
uint a, b, c;
a = b = c = 0xdeadbeefu + (4u << 2u) + 13u;
a += kx;
b += ky;
c += kz;
mix(a, b, c);
a += kw;
final(a, b, c);
return c;
}
#undef rot
#undef final
#undef mix
uint hash_int(int kx)
{
return hash_uint(uint(kx));
}
uint hash_int2(int kx, int ky)
{
return hash_uint2(uint(kx), uint(ky));
}
uint hash_int3(int kx, int ky, int kz)
{
return hash_uint3(uint(kx), uint(ky), uint(kz));
}
uint hash_int4(int kx, int ky, int kz, int kw)
{
return hash_uint4(uint(kx), uint(ky), uint(kz), uint(kw));
}
/* PCG 2D, 3D and 4D hash functions,
* from "Hash Functions for GPU Rendering" JCGT 2020
* https://jcgt.org/published/0009/03/02/
*
* Slightly modified to only use signed integers,
* so that they can also be implemented in OSL. */
int2 hash_pcg2d_i(int2 v)
{
v = v * 1664525 + 1013904223;
v.x += v.y * 1664525;
v.y += v.x * 1664525;
v = v ^ (v >> 16);
v.x += v.y * 1664525;
v.y += v.x * 1664525;
return v;
}
int3 hash_pcg3d_i(int3 v)
{
v = v * 1664525 + 1013904223;
v.x += v.y * v.z;
v.y += v.z * v.x;
v.z += v.x * v.y;
v = v ^ (v >> 16);
v.x += v.y * v.z;
v.y += v.z * v.x;
v.z += v.x * v.y;
return v;
}
int4 hash_pcg4d_i(int4 v)
{
v = v * 1664525 + 1013904223;
v.x += v.y * v.w;
v.y += v.z * v.x;
v.z += v.x * v.y;
v.w += v.y * v.z;
v = v ^ (v >> 16);
v.x += v.y * v.w;
v.y += v.z * v.x;
v.z += v.x * v.y;
v.w += v.y * v.z;
return v;
}
/* Hashing uint or uint[234] into a float in the range [0, 1]. */
float hash_uint_to_float(uint kx)
{
return float(hash_uint(kx)) / float(0xFFFFFFFFu);
}
float hash_uint2_to_float(uint kx, uint ky)
{
return float(hash_uint2(kx, ky)) / float(0xFFFFFFFFu);
}
float hash_uint3_to_float(uint kx, uint ky, uint kz)
{
return float(hash_uint3(kx, ky, kz)) / float(0xFFFFFFFFu);
}
float hash_uint4_to_float(uint kx, uint ky, uint kz, uint kw)
{
return float(hash_uint4(kx, ky, kz, kw)) / float(0xFFFFFFFFu);
}
/* Hashing float or vec[234] into a float in the range [0, 1]. */
float hash_float_to_float(float k)
{
return hash_uint_to_float(floatBitsToUint(k));
}
float hash_vec2_to_float(float2 k)
{
return hash_uint2_to_float(floatBitsToUint(k.x), floatBitsToUint(k.y));
}
float hash_vec3_to_float(float3 k)
{
return hash_uint3_to_float(floatBitsToUint(k.x), floatBitsToUint(k.y), floatBitsToUint(k.z));
}
float hash_vec4_to_float(float4 k)
{
return hash_uint4_to_float(
floatBitsToUint(k.x), floatBitsToUint(k.y), floatBitsToUint(k.z), floatBitsToUint(k.w));
}
/* Hashing vec[234] into vec[234] of components in the range [0, 1]. */
float2 hash_vec2_to_vec2(float2 k)
{
return float2(hash_vec2_to_float(k), hash_vec3_to_float(float3(k, 1.0f)));
}
float3 hash_vec3_to_vec3(float3 k)
{
return float3(hash_vec3_to_float(k),
hash_vec4_to_float(float4(k, 1.0f)),
hash_vec4_to_float(float4(k, 2.0f)));
}
float4 hash_vec4_to_vec4(float4 k)
{
return float4(hash_vec4_to_float(k.xyzw),
hash_vec4_to_float(k.wxyz),
hash_vec4_to_float(k.zwxy),
hash_vec4_to_float(k.yzwx));
}
/* Hashing a number of integers into floats in [0..1] range. */
float2 hash_int2_to_vec2(int2 k)
{
int2 h = hash_pcg2d_i(k);
return float2(h & 0x7fffffff) * (1.0 / float(0x7fffffff));
}
float3 hash_int3_to_vec3(int3 k)
{
int3 h = hash_pcg3d_i(k);
return float3(h & 0x7fffffff) * (1.0 / float(0x7fffffff));
}
float4 hash_int4_to_vec4(int4 k)
{
int4 h = hash_pcg4d_i(k);
return float4(h & 0x7fffffff) * (1.0 / float(0x7fffffff));
}
float3 hash_int2_to_vec3(int2 k)
{
return hash_int3_to_vec3(int3(k.x, k.y, 0));
}
float3 hash_int4_to_vec3(int4 k)
{
return hash_int4_to_vec4(k).xyz;
}
/* Hashing float or vec[234] into vec3 of components in range [0, 1]. */
float3 hash_float_to_vec3(float k)
{
return float3(hash_float_to_float(k),
hash_vec2_to_float(float2(k, 1.0f)),
hash_vec2_to_float(float2(k, 2.0f)));
}
float3 hash_vec2_to_vec3(float2 k)
{
return float3(hash_vec2_to_float(k),
hash_vec3_to_float(float3(k, 1.0f)),
hash_vec3_to_float(float3(k, 2.0f)));
}
float3 hash_vec4_to_vec3(float4 k)
{
return float3(
hash_vec4_to_float(k.xyzw), hash_vec4_to_float(k.zxwy), hash_vec4_to_float(k.wzyx));
}
/* Hashing float or vec[234] into vec2 of components in range [0, 1]. */
float2 hash_float_to_vec2(float k)
{
return float2(hash_float_to_float(k), hash_vec2_to_float(float2(k, 1.0f)));
}
float2 hash_vec3_to_vec2(float3 k)
{
return float2(hash_vec3_to_float(k.xyz), hash_vec3_to_float(k.zxy));
}
float2 hash_vec4_to_vec2(float4 k)
{
return float2(hash_vec4_to_float(k.xyzw), hash_vec4_to_float(k.zxwy));
}
/* Other Hash Functions */
float integer_noise(int n)
{
/* Integer bit-shifts for these calculations can cause precision problems on macOS.
* Using uint resolves these issues. */
uint nn;
nn = (uint(n) + 1013u) & 0x7fffffffu;
nn = (nn >> 13u) ^ nn;
nn = (uint(nn * (nn * nn * 60493u + 19990303u)) + 1376312589u) & 0x7fffffffu;
return 0.5f * (float(nn) / 1073741824.0f);
}
float wang_hash_noise(uint s)
{
s = (s ^ 61u) ^ (s >> 16u);
s *= 9u;
s = s ^ (s >> 4u);
s *= 0x27d4eb2du;
s = s ^ (s >> 15u);
return fract(float(s) / 4294967296.0f);
}

View File

@@ -0,0 +1,289 @@
/* SPDX-FileCopyrightText: 2019-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
#include "gpu_shader_math_base_lib.glsl"
#include "gpu_shader_math_safe_lib.glsl"
[[node]]
void math_add(float a, float b, float c, float &result)
{
result = a + b;
}
[[node]]
void math_subtract(float a, float b, float c, float &result)
{
result = a - b;
}
[[node]]
void math_multiply(float a, float b, float c, float &result)
{
result = a * b;
}
[[node]]
void math_divide(float a, float b, float c, float &result)
{
result = safe_divide(a, b);
}
[[node]]
void math_power(float a, float b, float c, float &result)
{
if (a >= 0.0f) {
result = compatible_pow(a, b);
}
else {
float fraction = mod(abs(b), 1.0f);
if (fraction > 0.999f || fraction < 0.001f) {
result = compatible_pow(a, floor(b + 0.5f));
}
else {
result = 0.0f;
}
}
}
[[node]]
void math_logarithm(float a, float b, float c, float &result)
{
result = (a > 0.0f && b > 0.0f) ? log2(a) / log2(b) : 0.0f;
}
[[node]]
void math_sqrt(float a, float b, float c, float &result)
{
result = (a > 0.0f) ? sqrt(a) : 0.0f;
}
[[node]]
void math_inversesqrt(float a, float b, float c, float &result)
{
result = inversesqrt(a);
}
[[node]]
void math_absolute(float a, float b, float c, float &result)
{
result = abs(a);
}
[[node]]
void math_radians(float a, float b, float c, float &result)
{
result = radians(a);
}
[[node]]
void math_degrees(float a, float b, float c, float &result)
{
result = degrees(a);
}
[[node]]
void math_minimum(float a, float b, float c, float &result)
{
result = min(a, b);
}
[[node]]
void math_maximum(float a, float b, float c, float &result)
{
result = max(a, b);
}
[[node]]
void math_less_than(float a, float b, float c, float &result)
{
result = (a < b) ? 1.0f : 0.0f;
}
[[node]]
void math_greater_than(float a, float b, float c, float &result)
{
result = (a > b) ? 1.0f : 0.0f;
}
[[node]]
void math_round(float a, float b, float c, float &result)
{
result = floor(a + 0.5f);
}
[[node]]
void math_floor(float a, float b, float c, float &result)
{
result = floor(a);
}
[[node]]
void math_ceil(float a, float b, float c, float &result)
{
result = ceil(a);
}
[[node]]
void math_fraction(float a, float b, float c, float &result)
{
result = a - floor(a);
}
[[node]]
void math_modulo(float a, float b, float c, float &result)
{
result = compatible_mod(a, b);
}
[[node]]
void math_floored_modulo(float a, float b, float c, float &result)
{
result = (b != 0.0f) ? a - floor(a / b) * b : 0.0f;
}
[[node]]
void math_trunc(float a, float b, float c, float &result)
{
result = trunc(a);
}
[[node]]
void math_snap(float a, float b, float c, float &result)
{
result = floor(safe_divide(a, b)) * b;
}
[[node]]
void math_pingpong(float a, float b, float c, float &result)
{
result = (b != 0.0f) ? abs(fract((a - b) / (b * 2.0f)) * b * 2.0f - b) : 0.0f;
}
/* Adapted from GODOT-engine math_funcs.h. */
[[node]]
void math_wrap(float a, float b, float c, float &result)
{
result = wrap(a, b, c);
}
[[node]]
void math_sine(float a, float b, float c, float &result)
{
result = sin(a);
}
[[node]]
void math_cosine(float a, float b, float c, float &result)
{
result = cos(a);
}
[[node]]
void math_tangent(float a, float b, float c, float &result)
{
result = tan(a);
}
[[node]]
void math_sinh(float a, float b, float c, float &result)
{
result = sinh(a);
}
[[node]]
void math_cosh(float a, float b, float c, float &result)
{
result = cosh(a);
}
[[node]]
void math_tanh(float a, float b, float c, float &result)
{
result = tanh(a);
}
[[node]]
void math_arcsine(float a, float b, float c, float &result)
{
result = (a <= 1.0f && a >= -1.0f) ? asin(a) : 0.0f;
}
[[node]]
void math_arccosine(float a, float b, float c, float &result)
{
result = (a <= 1.0f && a >= -1.0f) ? acos(a) : 0.0f;
}
[[node]]
void math_arctangent(float a, float b, float c, float &result)
{
result = atan(a);
}
/* The behavior of `atan2(0, 0)` is undefined on many platforms, to ensure consistent behavior, we
* return 0 in this case. See !126951. */
[[node]]
void math_arctan2(float a, float b, float c, float &result)
{
result = ((a == 0.0f && b == 0.0f) ? 0.0f : atan(a, b));
}
[[node]]
void math_sign(float a, float b, float c, float &result)
{
result = sign(a);
}
[[node]]
void math_exponent(float a, float b, float c, float &result)
{
result = exp(a);
}
[[node]]
void math_compare(float a, float b, float c, float &result)
{
result = (abs(a - b) <= max(c, 1e-5f)) ? 1.0f : 0.0f;
}
[[node]]
void math_multiply_add(float a, float b, float c, float &result)
{
result = a * b + c;
}
/* See: https://www.iquilezles.org/www/articles/smin/smin.htm. */
[[node]]
void math_smoothmin(float a, float b, float c, float &result)
{
if (c != 0.0f) {
float h = max(c - abs(a - b), 0.0f) / c;
result = min(a, b) - h * h * h * c * (1.0f / 6.0f);
}
else {
result = min(a, b);
}
}
[[node]]
void math_smoothmax(float a, float b, float c, float &result)
{
math_smoothmin(-a, -b, c, result);
result = -result;
}
/* TODO(fclem): Fix dependency hell one EEVEE legacy is removed. */
float math_reduce_max(float3 a)
{
return max(a.x, max(a.y, a.z));
}
float math_average(float3 a)
{
return (a.x + a.y + a.z) * (1.0f / 3.0f);
}

View File

@@ -0,0 +1,28 @@
/* SPDX-FileCopyrightText: 2019-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
[[node]]
void invert_z(float3 v, float3 &outv)
{
v.z = -v.z;
outv = v;
}
[[node]]
void vector_normalize(float3 normal, float3 &outnormal)
{
/* Match the safe normalize function in Cycles by defaulting to float3(0.0f) */
float length_sqr = dot(normal, normal);
outnormal = (length_sqr > 1e-35f) ? normal * inversesqrt(length_sqr) : float3(0.0f);
}
[[node]]
void vector_copy(float3 normal, float3 &outnormal)
{
outnormal = normal;
}

View File

@@ -0,0 +1,335 @@
/* SPDX-FileCopyrightText: 2019-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
#include "gpu_shader_common_color_utils.glsl"
[[node]]
void mix_blend(float fac, float4 col1, float4 col2, float4 &outcol)
{
outcol = mix(col1, col2, fac);
outcol.a = col1.a;
}
[[node]]
void mix_add(float fac, float4 col1, float4 col2, float4 &outcol)
{
outcol = mix(col1, col1 + col2, fac);
outcol.a = col1.a;
}
[[node]]
void mix_mult(float fac, float4 col1, float4 col2, float4 &outcol)
{
outcol = mix(col1, col1 * col2, fac);
outcol.a = col1.a;
}
[[node]]
void mix_screen(float fac, float4 col1, float4 col2, float4 &outcol)
{
float facm = 1.0f - fac;
outcol = float4(1.0f) - (float4(facm) + fac * (float4(1.0f) - col2)) * (float4(1.0f) - col1);
outcol.a = col1.a;
}
[[node]]
void mix_overlay(float fac, float4 col1, float4 col2, float4 &outcol)
{
float facm = 1.0f - fac;
outcol = col1;
if (outcol.r < 0.5f) {
outcol.r *= facm + 2.0f * fac * col2.r;
}
else {
outcol.r = 1.0f - (facm + 2.0f * fac * (1.0f - col2.r)) * (1.0f - outcol.r);
}
if (outcol.g < 0.5f) {
outcol.g *= facm + 2.0f * fac * col2.g;
}
else {
outcol.g = 1.0f - (facm + 2.0f * fac * (1.0f - col2.g)) * (1.0f - outcol.g);
}
if (outcol.b < 0.5f) {
outcol.b *= facm + 2.0f * fac * col2.b;
}
else {
outcol.b = 1.0f - (facm + 2.0f * fac * (1.0f - col2.b)) * (1.0f - outcol.b);
}
}
[[node]]
void mix_sub(float fac, float4 col1, float4 col2, float4 &outcol)
{
outcol = mix(col1, col1 - col2, fac);
outcol.a = col1.a;
}
[[node]]
void mix_div(float fac, float4 col1, float4 col2, float4 &outcol)
{
float facm = 1.0f - fac;
outcol = float4(float3(0.0f), col1.a);
if (col2.r != 0.0f) {
outcol.r = facm * col1.r + fac * col1.r / col2.r;
}
if (col2.g != 0.0f) {
outcol.g = facm * col1.g + fac * col1.g / col2.g;
}
if (col2.b != 0.0f) {
outcol.b = facm * col1.b + fac * col1.b / col2.b;
}
}
/* A variant of mix_div that fallback to the first color upon zero division. */
[[node]]
void mix_div_fallback(float fac, float4 col1, float4 col2, float4 &outcol)
{
float facm = 1.0f - fac;
outcol = col1;
if (col2.r != 0.0f) {
outcol.r = facm * outcol.r + fac * outcol.r / col2.r;
}
if (col2.g != 0.0f) {
outcol.g = facm * outcol.g + fac * outcol.g / col2.g;
}
if (col2.b != 0.0f) {
outcol.b = facm * outcol.b + fac * outcol.b / col2.b;
}
}
[[node]]
void mix_diff(float fac, float4 col1, float4 col2, float4 &outcol)
{
outcol = mix(col1, abs(col1 - col2), fac);
outcol.a = col1.a;
}
[[node]]
void mix_exclusion(float fac, float4 col1, float4 col2, float4 &outcol)
{
outcol = max(mix(col1, col1 + col2 - 2.0f * col1 * col2, fac), 0.0f);
outcol.a = col1.a;
}
[[node]]
void mix_dark(float fac, float4 col1, float4 col2, float4 &outcol)
{
outcol.rgb = mix(col1.rgb, min(col1.rgb, col2.rgb), fac);
outcol.a = col1.a;
}
[[node]]
void mix_light(float fac, float4 col1, float4 col2, float4 &outcol)
{
outcol.rgb = mix(col1.rgb, max(col1.rgb, col2.rgb), fac);
outcol.a = col1.a;
}
[[node]]
void mix_dodge(float fac, float4 col1, float4 col2, float4 &outcol)
{
outcol = col1;
if (outcol.r != 0.0f) {
float tmp = 1.0f - fac * col2.r;
if (tmp <= 0.0f) {
outcol.r = 1.0f;
}
else if ((tmp = outcol.r / tmp) > 1.0f) {
outcol.r = 1.0f;
}
else {
outcol.r = tmp;
}
}
if (outcol.g != 0.0f) {
float tmp = 1.0f - fac * col2.g;
if (tmp <= 0.0f) {
outcol.g = 1.0f;
}
else if ((tmp = outcol.g / tmp) > 1.0f) {
outcol.g = 1.0f;
}
else {
outcol.g = tmp;
}
}
if (outcol.b != 0.0f) {
float tmp = 1.0f - fac * col2.b;
if (tmp <= 0.0f) {
outcol.b = 1.0f;
}
else if ((tmp = outcol.b / tmp) > 1.0f) {
outcol.b = 1.0f;
}
else {
outcol.b = tmp;
}
}
}
[[node]]
void mix_burn(float fac, float4 col1, float4 col2, float4 &outcol)
{
float tmp, facm = 1.0f - fac;
outcol = col1;
tmp = facm + fac * col2.r;
if (tmp <= 0.0f) {
outcol.r = 0.0f;
}
else if ((tmp = (1.0f - (1.0f - outcol.r) / tmp)) < 0.0f) {
outcol.r = 0.0f;
}
else if (tmp > 1.0f) {
outcol.r = 1.0f;
}
else {
outcol.r = tmp;
}
tmp = facm + fac * col2.g;
if (tmp <= 0.0f) {
outcol.g = 0.0f;
}
else if ((tmp = (1.0f - (1.0f - outcol.g) / tmp)) < 0.0f) {
outcol.g = 0.0f;
}
else if (tmp > 1.0f) {
outcol.g = 1.0f;
}
else {
outcol.g = tmp;
}
tmp = facm + fac * col2.b;
if (tmp <= 0.0f) {
outcol.b = 0.0f;
}
else if ((tmp = (1.0f - (1.0f - outcol.b) / tmp)) < 0.0f) {
outcol.b = 0.0f;
}
else if (tmp > 1.0f) {
outcol.b = 1.0f;
}
else {
outcol.b = tmp;
}
}
[[node]]
void mix_hue(float fac, float4 col1, float4 col2, float4 &outcol)
{
float facm = 1.0f - fac;
outcol = col1;
float4 hsv, hsv2, tmp;
rgb_to_hsv(col2, hsv2);
if (hsv2.y != 0.0f) {
rgb_to_hsv(outcol, hsv);
hsv.x = hsv2.x;
hsv_to_rgb(hsv, tmp);
outcol = mix(outcol, tmp, fac);
outcol.a = col1.a;
}
}
[[node]]
void mix_sat(float fac, float4 col1, float4 col2, float4 &outcol)
{
float facm = 1.0f - fac;
outcol = col1;
float4 hsv, hsv2;
rgb_to_hsv(outcol, hsv);
if (hsv.y != 0.0f) {
rgb_to_hsv(col2, hsv2);
hsv.y = facm * hsv.y + fac * hsv2.y;
hsv_to_rgb(hsv, outcol);
}
}
[[node]]
void mix_val(float fac, float4 col1, float4 col2, float4 &outcol)
{
float facm = 1.0f - fac;
float4 hsv, hsv2;
rgb_to_hsv(col1, hsv);
rgb_to_hsv(col2, hsv2);
hsv.z = facm * hsv.z + fac * hsv2.z;
hsv_to_rgb(hsv, outcol);
}
[[node]]
void mix_color(float fac, float4 col1, float4 col2, float4 &outcol)
{
float facm = 1.0f - fac;
outcol = col1;
float4 hsv, hsv2, tmp;
rgb_to_hsv(col2, hsv2);
if (hsv2.y != 0.0f) {
rgb_to_hsv(outcol, hsv);
hsv.x = hsv2.x;
hsv.y = hsv2.y;
hsv_to_rgb(hsv, tmp);
outcol = mix(outcol, tmp, fac);
outcol.a = col1.a;
}
}
[[node]]
void mix_soft(float fac, float4 col1, float4 col2, float4 &outcol)
{
float facm = 1.0f - fac;
float4 one = float4(1.0f);
float4 scr = one - (one - col2) * (one - col1);
outcol = facm * col1 + fac * ((one - col1) * col2 * col1 + col1 * scr);
outcol.a = col1.a;
}
[[node]]
void mix_linear(float fac, float4 col1, float4 col2, float4 &outcol)
{
outcol = col1 + fac * (2.0f * (col2 - float4(0.5f)));
outcol.a = col1.a;
}
[[node]]
void clamp_color(float4 vec, const float4 min, const float4 max, float4 &out_vec)
{
out_vec = clamp(vec, min, max);
}
[[node]]
void multiply_by_alpha(float factor, float4 color, float &result)
{
result = factor * color.a;
}

View File

@@ -0,0 +1,36 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
/*
* For debugging purpose mainly.
* From https://www.shadertoy.com/view/4dsSzr
* By Morgan McGuire @morgan3d, http://graphicscodex.com
* Reuse permitted under the BSD license.
*/
float3 neon_gradient(float t)
{
float tt = abs(0.43f - t) * 1.7f;
return clamp(float3(t * 1.3f + 0.1f, tt * tt, (1.0f - t) * 1.7f), 0.0f, 1.0f);
}
float3 heatmap_gradient(float t)
{
float a = pow(t, 1.5f) * 0.8f + 0.2f;
float b = smoothstep(0.0f, 0.35f, t) + t * 0.5f;
float c = smoothstep(0.5f, 1.0f, t);
float d = max(1.0f - t * 1.7f, t * 7.0f - 6.0f);
return clamp(a * float3(b, c, d), float3(0.0f), float3(1.0f));
}
float3 hue_gradient(float t)
{
float3 p = abs(fract(t + float3(1.0f, 2.0f / 3.0f, 1.0f / 3.0f)) * 6.0f - 3.0f);
return (clamp(p - 1.0f, 0.0f, 1.0f));
}
float3 green_to_red_gradient(float t)
{
return mix(float3(0.0f, 1.0f, 0.0f), float3(1.0f, 0.0f, 0.0f), t);
}

View File

@@ -0,0 +1,22 @@
/* SPDX-FileCopyrightText: 2015-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
/* Position vertices of a triangle to cover the whole screen. */
void fullscreen_vertex(int vertex_id, float4 &out_position)
{
int v = vertex_id % 3;
float x = -1.0f + float((v & 1) << 2);
float y = -1.0f + float((v & 2) << 1);
out_position = float4(x, y, 1.0f, 1.0f);
}
void fullscreen_vertex(int vertex_id, float4 &out_position, float2 &out_uv)
{
fullscreen_vertex(vertex_id, out_position);
out_uv = (out_position.xy + 1.0f) * 0.5f;
}

View File

@@ -0,0 +1,15 @@
/* SPDX-FileCopyrightText: 2015-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_fullscreen_infos.hh"
#include "gpu_shader_sequencer_infos.hh"
VERTEX_SHADER_CREATE_INFO(gpu_fullscreen)
#include "gpu_shader_fullscreen_lib.glsl"
void main()
{
fullscreen_vertex(gl_VertexID, gl_Position, screen_uv);
}

View File

@@ -0,0 +1,41 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "infos/gpu_index_load_infos.hh"
SHADER_LIBRARY_CREATE_INFO(gpu_index_buffer_load)
/**
* Library to read the index buffer of a `gpu::Batch` using a SSBO rather than using `gl_VertexID`.
* This is required for primitive expansion without geometry shader.
* It is **not** needed if it is guaranteed that the processed `gpu::Batch` will not use any index
* buffer.
*/
#ifndef WORKAROUND_INDEX_LOAD_INCLUDE
# ifndef GPU_INDEX_LOAD
# error Missing gpu_index_buffer_load create info dependency
# endif
/**
* Returns the resolved index after index buffer (a.k.a. element buffer) indirection.
*/
uint gpu_index_load(uint element_index)
{
if (gpu_index_no_buffer) {
return element_index;
}
uint raw_index = gpu_index_buf[gpu_index_16bit ? element_index >> 1u : element_index];
if (gpu_index_16bit) {
raw_index = ((element_index & 1u) == 0u) ? (raw_index & 0xFFFFu) : (raw_index >> 16u);
}
return raw_index + uint(gpu_index_base_index);
}
#endif

View File

@@ -0,0 +1,77 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
/* WORKAROUND: Workaround include order hell. */
#ifdef GLSL_CPP_STUBS
#elif defined(GPU_SHADER)
# define static
#endif
struct IndexRange {
int start_;
int size_;
static IndexRange from_begin_end(int begin, int end)
{
return {begin, end - begin};
}
/**
* Get the first element in the range.
*/
int first() const
{
return this->start_;
}
/**
* Get the first element in the range. The returned value is undefined when the range is empty.
*/
int start() const
{
return this->start_;
}
/**
* Get the nth last element in the range.
*/
int last(int n = 0) const
{
return this->start_ + this->size_ - 1 - n;
}
/**
* Get the amount of numbers in the range.
*/
int size() const
{
return this->size_;
}
/**
* Returns a new range, that contains a sub-interval of the current one.
*/
IndexRange slice(int start, int size) const
{
int new_start = this->start_ + start;
return {new_start, size};
}
IndexRange slice(IndexRange range) const
{
return this->slice(range.start(), range.size());
}
/**
* Move the range forward or backward within the larger array. The amount may be negative,
* but its absolute value cannot be greater than the existing start of the range.
*/
IndexRange shift(int n) const
{
return {this->start_ + n, this->size_};
}
};

View File

@@ -0,0 +1,24 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
#include "gpu_shader_math_constants_lib.glsl"
struct AngleRadian {
/* Note that value is public because of the lack of casting operator in GLSL. */
float angle;
static AngleRadian identity()
{
return {0};
}
static AngleRadian from_degree(float angle_degree)
{
return {angle_degree * (M_PI / 180.0f)};
}
};

View File

@@ -0,0 +1,17 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
struct AxisAngle {
float3 axis;
float angle;
static AxisAngle identity()
{
return {float3(0, 1, 0), 0};
}
};

View File

@@ -0,0 +1,128 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
/* `powf` is really slow for raising to integer powers. */
float pow2f(float x)
{
return x * x;
}
float pow3f(float x)
{
return x * x * x;
}
float pow4f(float x)
{
return pow2f(pow2f(x));
}
float pow5f(float x)
{
return pow4f(x) * x;
}
float pow6f(float x)
{
return pow2f(pow3f(x));
}
float pow7f(float x)
{
return pow6f(x) * x;
}
float pow8f(float x)
{
return pow2f(pow4f(x));
}
float square(float v)
{
return v * v;
}
float2 square(float2 v)
{
return v * v;
}
float3 square(float3 v)
{
return v * v;
}
float4 square(float4 v)
{
return v * v;
}
float hypot(float x, float y)
{
return sqrt(x * x + y * y);
}
/* Declared as _atan2 to prevent errors with `WITH_GPU_SHADER_CPP_COMPILATION` on VS2019 due
* to `corecrt_math` conflicting functions. */
float _atan2(float y, float x)
{
return atan(y, x);
}
#define atan2 _atan2
/**
* Returns \a a if it is a multiple of \a b or the next multiple or \a b after \b a .
* In other words, it is equivalent to `divide_ceil(a, b) * b`.
* It is undefined if \a a is negative or \b b is not strictly positive.
*/
int ceil_to_multiple(int a, int b)
{
return ((a + b - 1) / b) * b;
}
uint ceil_to_multiple(uint a, uint b)
{
return ((a + b - 1u) / b) * b;
}
/**
* Integer division that returns the ceiling, instead of flooring like normal C division.
* It is undefined if \a a is negative or \b b is not strictly positive.
*/
int divide_ceil(int a, int b)
{
return (a + b - 1) / b;
}
uint divide_ceil(uint a, uint b)
{
return (a + b - 1u) / b;
}
/**
* Component wise, use vector to replace min if it is smaller and max if bigger.
*/
void min_max(float value, float &min_v, float &max_v)
{
min_v = min(value, min_v);
max_v = max(value, max_v);
}
/**
* Return true if the difference between`a` and `b` is below the `epsilon` value.
*/
bool is_equal(float a, float b, const float epsilon)
{
return abs(a - b) <= epsilon;
}
float sin_from_cos(float c)
{
return sqrt(max(0.0f, 1.0f - square(c)));
}
float cos_from_sin(float s)
{
return sqrt(max(0.0f, 1.0f - square(s)));
}
float cos_from_tan(float t)
{
return inversesqrt(1.0f + square(t));
}

View File

@@ -0,0 +1,20 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#define M_PI 3.14159265358979323846f /* pi */
#define M_TAU 6.28318530717958647692f /* tau = 2*pi */
#define M_PI_2 1.57079632679489661923f /* pi/2 */
#define M_PI_4 0.78539816339744830962f /* pi/4 */
#define M_SQRT2 1.41421356237309504880f /* sqrt(2) */
#define M_SQRT1_2 0.70710678118654752440f /* 1/sqrt(2) */
#define M_SQRT3 1.73205080756887729352f /* sqrt(3) */
#define M_SQRT1_3 0.57735026918962576450f /* 1/sqrt(3) */
#define M_1_PI 0.318309886183790671538f /* 1/pi */
#define M_E 2.7182818284590452354f /* e */
#define M_LOG2E 1.4426950408889634074f /* log_2 e */
#define M_LOG10E 0.43429448190325182765f /* log_10 e */
#define M_LN2 0.69314718055994530942f /* log_e 2 */
#define M_LN10 2.30258509299404568402f /* log_e 10 */

View File

@@ -0,0 +1,26 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
struct EulerXYZ {
float x, y, z;
static EulerXYZ from_float3(float3 eul)
{
return {eul.x, eul.y, eul.z};
}
static EulerXYZ identity()
{
return {0, 0, 0};
}
float3 as_float3() const
{
return float3(this->x, this->y, this->z);
}
};

View File

@@ -0,0 +1,53 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
#include "gpu_shader_math_constants_lib.glsl"
/* [Drobot2014a] Low Level Optimizations for GCN. */
float sqrt_fast(float v)
{
return intBitsToFloat(0x1fbd1df5 + (floatBitsToInt(v) >> 1));
}
float2 sqrt_fast(float2 v)
{
return intBitsToFloat(0x1fbd1df5 + (floatBitsToInt(v) >> 1));
}
/* [Eberly2014] GPGPU Programming for Games and Science. */
float acos_fast(float v)
{
float res = -0.156583f * abs(v) + M_PI_2;
res *= sqrt_fast(1.0f - abs(v));
return (v >= 0) ? res : M_PI - res;
}
float2 acos_fast(float2 v)
{
float2 res = -0.156583f * abs(v) + M_PI_2;
res *= sqrt_fast(1.0f - abs(v));
v.x = (v.x >= 0) ? res.x : M_PI - res.x;
v.y = (v.y >= 0) ? res.y : M_PI - res.y;
return v;
}
float atan_fast(float x)
{
float a = abs(x);
float k = a > 1.0f ? (1.0f / a) : a;
float s = 1.0f - (1.0f - k); /* Crush denormals. */
float t = s * s;
/* http://mathforum.org/library/drmath/view/62672.html
* Examined 4278190080 values of atan:
* 2.36864877 avg ULP diff, 302 max ULP, 6.55651e-06f max error // (with denormals)
* Examined 4278190080 values of atan:
* 171160502 avg ULP diff, 855638016 max ULP, 6.55651e-06f max error // (crush denormals)
*/
float r = s * fma(0.43157974f, t, 1.0f) / fma(fma(0.05831938f, t, 0.76443945f), t, 1.0f);
if (a > 1.0f) {
r = 1.57079632679489661923f - r;
}
return (x < 0.0f) ? -r : r;
}

View File

@@ -0,0 +1,92 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
/* -------------------------------------------------------------------- */
/** \name Normalize
* \{ */
/**
* Returns the adjoint of the matrix (also known as adjugate matrix).
*/
float2x2 adjoint(float2x2 mat)
{
float2x2 adj = float2x2(0.0f);
for (int c = 0; c < 2; c++) {
for (int r = 0; r < 2; r++) {
/* Copy other cells except the "cross" to compute the determinant. */
float tmp = 0.0f;
for (int m_c = 0; m_c < 2; m_c++) {
for (int m_r = 0; m_r < 2; m_r++) {
if (m_c != c && m_r != r) {
tmp = mat[m_c][m_r];
}
}
}
float minor = tmp;
/* Transpose directly to get the adjugate. Swap destination row and col. */
adj[r][c] = (((c + r) & 1) != 0) ? -minor : minor;
}
}
return adj;
}
/**
* Returns the adjoint of the matrix (also known as adjugate matrix).
*/
float3x3 adjoint(float3x3 mat)
{
float3x3 adj = float3x3(0.0f);
for (int c = 0; c < 3; c++) {
for (int r = 0; r < 3; r++) {
/* Copy other cells except the "cross" to compute the determinant. */
float2x2 tmp = float2x2(0.0f);
for (int m_c = 0; m_c < 3; m_c++) {
for (int m_r = 0; m_r < 3; m_r++) {
if (m_c != c && m_r != r) {
int d_c = (m_c < c) ? m_c : (m_c - 1);
int d_r = (m_r < r) ? m_r : (m_r - 1);
tmp[d_c][d_r] = mat[m_c][m_r];
}
}
}
float minor = determinant(tmp);
/* Transpose directly to get the adjugate. Swap destination row and col. */
adj[r][c] = (((c + r) & 1) != 0) ? -minor : minor;
}
}
return adj;
}
/**
* Returns the adjoint of the matrix (also known as adjugate matrix).
*/
float4x4 adjoint(float4x4 mat)
{
float4x4 adj = float4x4(0.0f);
for (int c = 0; c < 4; c++) {
for (int r = 0; r < 4; r++) {
/* Copy other cells except the "cross" to compute the determinant. */
float3x3 tmp = float3x3(0.0f);
for (int m_c = 0; m_c < 4; m_c++) {
for (int m_r = 0; m_r < 4; m_r++) {
if (m_c != c && m_r != r) {
int d_c = (m_c < c) ? m_c : (m_c - 1);
int d_r = (m_r < r) ? m_r : (m_r - 1);
tmp[d_c][d_r] = mat[m_c][m_r];
}
}
}
float minor = determinant(tmp);
/* Transpose directly to get the adjugate. Swap destination row and col. */
adj[r][c] = (((c + r) & 1) != 0) ? -minor : minor;
}
}
return adj;
}
/** \} */

View File

@@ -0,0 +1,214 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_math_vector_compare_lib.glsl"
/* -------------------------------------------------------------------- */
/** \name Compare / Test
* \{ */
/**
* Returns true if all of the matrices components are strictly equal to 0.
*/
bool is_zero(float3x3 a)
{
if (is_zero(a[0])) {
if (is_zero(a[1])) {
if (is_zero(a[2])) {
return true;
}
}
}
return false;
}
/**
* Returns true if all of the matrices components are strictly equal to 0.
*/
bool is_zero(float4x4 a)
{
if (is_zero(a[0])) {
if (is_zero(a[1])) {
if (is_zero(a[2])) {
if (is_zero(a[3])) {
return true;
}
}
}
}
return false;
}
/**
* Returns true if matrix has inverted handedness.
*/
bool is_negative(float3x3 mat)
{
return determinant(mat) < 0.0f;
}
/**
* Returns true if matrix has inverted handedness.
*
* \note It doesn't use determinant(mat4x4) as only the 3x3 components are needed
* when the matrix is used as a transformation to represent location/scale/rotation.
*/
bool is_negative(float4x4 mat)
{
return is_negative(to_float3x3(mat));
}
/**
* Returns true if matrices are equal within the given epsilon.
*/
bool is_equal(float2x2 a, float2x2 b, float epsilon)
{
if (is_equal(a[0], b[0], epsilon)) {
if (is_equal(a[1], b[1], epsilon)) {
return true;
}
}
return false;
}
/**
* Returns true if matrices are equal within the given epsilon.
*/
bool is_equal(float3x3 a, float3x3 b, float epsilon)
{
if (is_equal(a[0], b[0], epsilon)) {
if (is_equal(a[1], b[1], epsilon)) {
if (is_equal(a[2], b[2], epsilon)) {
return true;
}
}
}
return false;
}
/**
* Returns true if matrices are equal within the given epsilon.
*/
bool is_equal(float4x4 a, float4x4 b, float epsilon)
{
if (is_equal(a[0], b[0], epsilon)) {
if (is_equal(a[1], b[1], epsilon)) {
if (is_equal(a[2], b[2], epsilon)) {
if (is_equal(a[3], b[3], epsilon)) {
return true;
}
}
}
}
return false;
}
/**
* Test if the X, Y and Z axes are perpendicular with each other.
*/
bool is_orthogonal(float3x3 mat)
{
if (abs(dot(mat[0], mat[1])) > 1e-5f) {
return false;
}
if (abs(dot(mat[1], mat[2])) > 1e-5f) {
return false;
}
if (abs(dot(mat[2], mat[0])) > 1e-5f) {
return false;
}
return true;
}
/**
* Test if the X, Y and Z axes are perpendicular with each other.
*/
bool is_orthogonal(float4x4 mat)
{
return is_orthogonal(to_float3x3(mat));
}
/**
* Test if the X, Y and Z axes are perpendicular with each other and unit length.
*/
bool is_orthonormal(float3x3 mat)
{
if (!is_orthogonal(mat)) {
return false;
}
if (abs(dot(mat[0], mat[0]) - 1.0f) > 1e-5f) {
return false;
}
if (abs(dot(mat[1], mat[1]) - 1.0f) > 1e-5f) {
return false;
}
if (abs(dot(mat[2], mat[2]) - 1.0f) > 1e-5f) {
return false;
}
return true;
}
/**
* Test if the X, Y and Z axes are perpendicular with each other and unit length.
*/
bool is_orthonormal(float4x4 mat)
{
return is_orthonormal(to_float3x3(mat));
}
/**
* Test if the X, Y and Z axes are perpendicular with each other and the same length.
*/
bool is_uniformly_scaled(float3x3 mat)
{
if (!is_orthogonal(mat)) {
return false;
}
constexpr float eps = 1e-7f;
float x = dot(mat[0], mat[0]);
float y = dot(mat[1], mat[1]);
float z = dot(mat[2], mat[2]);
return (abs(x - y) < eps) && abs(x - z) < eps;
}
/**
* Test if the X, Y and Z axes are perpendicular with each other and the same length.
*/
bool is_uniformly_scaled(float4x4 mat)
{
return is_uniformly_scaled(to_float3x3(mat));
}
/* Returns true if each individual columns are unit scaled. Mainly for assert usage. */
bool is_unit_scale(float4x4 m)
{
if (is_unit_scale(m[0])) {
if (is_unit_scale(m[1])) {
if (is_unit_scale(m[2])) {
if (is_unit_scale(m[3])) {
return true;
}
}
}
}
return false;
}
/* Returns true if each individual columns are unit scaled. Mainly for assert usage. */
bool is_unit_scale(float3x3 m)
{
if (is_unit_scale(m[0])) {
if (is_unit_scale(m[1])) {
if (is_unit_scale(m[2])) {
return true;
}
}
}
return false;
}
/* Returns true if each individual columns are unit scaled. Mainly for assert usage. */
bool is_unit_scale(float2x2 m)
{
if (is_unit_scale(m[0])) {
if (is_unit_scale(m[1])) {
return true;
}
}
return false;
}
/** \} */

View File

@@ -0,0 +1,346 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_math_angle_lib.glsl"
#include "gpu_shader_math_axis_angle_lib.glsl"
#include "gpu_shader_math_base_lib.glsl"
#include "gpu_shader_math_euler_lib.glsl"
#include "gpu_shader_math_quaternion_lib.glsl"
/* -------------------------------------------------------------------- */
/** \name Static constructors
* \{ */
float2x2 mat2x2_diagonal(float v)
{
return float2x2(float2(v, 0.0f), float2(0.0f, v));
}
float3x3 mat3x3_diagonal(float v)
{
return float3x3(float3(v, 0.0f, 0.0f), float3(0.0f, v, 0.0f), float3(0.0f, 0.0f, v));
}
float4x4 mat4x4_diagonal(float v)
{
return float4x4(float4(v, 0.0f, 0.0f, 0.0f),
float4(0.0f, v, 0.0f, 0.0f),
float4(0.0f, 0.0f, v, 0.0f),
float4(0.0f, 0.0f, 0.0f, v));
}
float2x2 mat2x2_all(float v)
{
return float2x2(float2(v), float2(v));
}
float3x3 mat3x3_all(float v)
{
return float3x3(float3(v), float3(v), float3(v));
}
float4x4 mat4x4_all(float v)
{
return float4x4(float4(v), float4(v), float4(v), float4(v));
}
float2x2 mat2x2_zero()
{
return mat2x2_all(0.0f);
}
float3x3 mat3x3_zero()
{
return mat3x3_all(0.0f);
}
float4x4 mat4x4_zero()
{
return mat4x4_all(0.0f);
}
float2x2 mat2x2_identity()
{
return mat2x2_diagonal(1.0f);
}
float3x3 mat3x3_identity()
{
return mat3x3_diagonal(1.0f);
}
float4x4 mat4x4_identity()
{
return mat4x4_diagonal(1.0f);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Init helpers.
* \{ */
/**
* Create a translation only matrix. Matrix dimensions should be at least 4 col x 3 row.
*/
float4x4 from_location(float3 location)
{
float4x4 ret = float4x4(1.0f);
ret[3].xyz = location;
return ret;
}
/**
* Create a matrix whose diagonal is defined by the given scale vector.
*/
float2x2 from_scale(float2 scale)
{
float2x2 ret = float2x2(0.0f);
ret[0][0] = scale[0];
ret[1][1] = scale[1];
return ret;
}
/**
* Create a matrix whose diagonal is defined by the given scale vector.
*/
float3x3 from_scale(float3 scale)
{
float3x3 ret = float3x3(0.0f);
ret[0][0] = scale[0];
ret[1][1] = scale[1];
ret[2][2] = scale[2];
return ret;
}
/**
* Create a matrix whose diagonal is defined by the given scale vector.
*/
float4x4 from_scale(float4 scale)
{
float4x4 ret = float4x4(0.0f);
ret[0][0] = scale[0];
ret[1][1] = scale[1];
ret[2][2] = scale[2];
ret[3][3] = scale[3];
return ret;
}
/**
* Create a rotation only matrix.
*/
float2x2 from_rotation(AngleRadian rotation)
{
float c = cos(rotation.angle);
float s = sin(rotation.angle);
return float2x2(c, -s, s, c);
}
/**
* Create a rotation only matrix.
*/
float3x3 from_rotation(EulerXYZ rotation)
{
float ci = cos(rotation.x);
float cj = cos(rotation.y);
float ch = cos(rotation.z);
float si = sin(rotation.x);
float sj = sin(rotation.y);
float sh = sin(rotation.z);
float cc = ci * ch;
float cs = ci * sh;
float sc = si * ch;
float ss = si * sh;
float3x3 mat;
mat[0][0] = cj * ch;
mat[1][0] = sj * sc - cs;
mat[2][0] = sj * cc + ss;
mat[0][1] = cj * sh;
mat[1][1] = sj * ss + cc;
mat[2][1] = sj * cs - sc;
mat[0][2] = -sj;
mat[1][2] = cj * si;
mat[2][2] = cj * ci;
return mat;
}
/**
* Create a rotation only matrix.
*/
float3x3 from_rotation(Quaternion rotation)
{
/* NOTE: Should be double but support isn't native on most GPUs. */
float q0 = M_SQRT2 * float(rotation.x);
float q1 = M_SQRT2 * float(rotation.y);
float q2 = M_SQRT2 * float(rotation.z);
float q3 = M_SQRT2 * float(rotation.w);
float qda = q0 * q1;
float qdb = q0 * q2;
float qdc = q0 * q3;
float qaa = q1 * q1;
float qab = q1 * q2;
float qac = q1 * q3;
float qbb = q2 * q2;
float qbc = q2 * q3;
float qcc = q3 * q3;
float3x3 mat;
mat[0][0] = float(1.0f - qbb - qcc);
mat[0][1] = float(qdc + qab);
mat[0][2] = float(-qdb + qac);
mat[1][0] = float(-qdc + qab);
mat[1][1] = float(1.0f - qaa - qcc);
mat[1][2] = float(qda + qbc);
mat[2][0] = float(qdb + qac);
mat[2][1] = float(-qda + qbc);
mat[2][2] = float(1.0f - qaa - qbb);
return mat;
}
/**
* Create a rotation only matrix.
*/
float3x3 from_rotation(AxisAngle rotation)
{
float angle_sin = sin(rotation.angle);
float angle_cos = cos(rotation.angle);
float3 axis = rotation.axis;
float ico = (float(1) - angle_cos);
float3 nsi = axis * angle_sin;
float3 n012 = (axis * axis) * ico;
float n_01 = (axis[0] * axis[1]) * ico;
float n_02 = (axis[0] * axis[2]) * ico;
float n_12 = (axis[1] * axis[2]) * ico;
float3x3 mat = from_scale(n012 + angle_cos);
mat[0][1] = n_01 + nsi[2];
mat[0][2] = n_02 - nsi[1];
mat[1][0] = n_01 - nsi[2];
mat[1][2] = n_12 + nsi[0];
mat[2][0] = n_02 + nsi[1];
mat[2][1] = n_12 - nsi[0];
return mat;
}
/**
* Create a transform matrix with rotation and scale applied in this order.
*/
float3x3 from_rot_scale(EulerXYZ rotation, float3 scale)
{
return from_rotation(rotation) * from_scale(scale);
}
/**
* Create a transform matrix with rotation and scale applied in this order.
*/
float3x3 from_rot_scale(Quaternion rotation, float3 scale)
{
return from_rotation(rotation) * from_scale(scale);
}
/**
* Create a transform matrix with rotation and scale applied in this order.
*/
float3x3 from_rot_scale(AxisAngle rotation, float3 scale)
{
return from_rotation(rotation) * from_scale(scale);
}
/**
* Create a transform matrix with translation and rotation applied in this order.
*/
float4x4 from_loc_rot(float3 location, EulerXYZ rotation)
{
float4x4 ret = to_float4x4(from_rotation(rotation));
ret[3].xyz = location;
return ret;
}
/**
* Create a transform matrix with translation and rotation applied in this order.
*/
float4x4 from_loc_rot(float3 location, Quaternion rotation)
{
float4x4 ret = to_float4x4(from_rotation(rotation));
ret[3].xyz = location;
return ret;
}
/**
* Create a transform matrix with translation and rotation applied in this order.
*/
float4x4 from_loc_rot(float3 location, AxisAngle rotation)
{
float4x4 ret = to_float4x4(from_rotation(rotation));
ret[3].xyz = location;
return ret;
}
/**
* Create a transform matrix with translation, rotation and scale applied in this order.
*/
float4x4 from_loc_rot_scale(float3 location, EulerXYZ rotation, float3 scale)
{
float4x4 ret = to_float4x4(from_rot_scale(rotation, scale));
ret[3].xyz = location;
return ret;
}
/**
* Create a transform matrix with translation, rotation and scale applied in this order.
*/
float4x4 from_loc_rot_scale(float3 location, Quaternion rotation, float3 scale)
{
float4x4 ret = to_float4x4(from_rot_scale(rotation, scale));
ret[3].xyz = location;
return ret;
}
/**
* Create a transform matrix with translation, rotation and scale applied in this order.
*/
float4x4 from_loc_rot_scale(float3 location, AxisAngle rotation, float3 scale)
{
float4x4 ret = to_float4x4(from_rot_scale(rotation, scale));
ret[3].xyz = location;
return ret;
}
/**
* Creates a 2D rotation matrix with the angle that the given direction makes with the x axis.
* Assumes the direction vector is normalized.
*/
float2x2 from_direction(float2 direction)
{
float cos_angle = direction.x;
float sin_angle = direction.y;
return float2x2(cos_angle, sin_angle, -sin_angle, cos_angle);
}
/**
* Create a rotation matrix from 2 basis vectors.
* The matrix determinant is given to be positive and it can be converted to other rotation types.
* \note `forward` and `up` must be normalized.
*/
// mat3x3 from_normalized_axis_data(vec3 forward, vec3 up); /* TODO. */
/**
* Create a transform matrix with translation and rotation from 2 basis vectors and a translation.
* \note `forward` and `up` must be normalized.
*/
// mat4x4 from_normalized_axis_data(vec3 location, vec3 forward, vec3 up); /* TODO. */
/**
* Create a rotation matrix from only one \a up axis.
* The other axes are chosen to always be orthogonal. The resulting matrix is a basis matrix.
* \note `up` must be normalized.
* \note This can be used to create a tangent basis from a normal vector.
* \note The output of this function is not given to be same across blender version. Prefer using
* `from_orthonormal_axes` for more stable output.
*/
float3x3 from_up_axis(float3 up)
{
/* Duff, Tom, et al. "Building an orthonormal basis, revisited." JCGT 6.1 (2017). */
float z_sign = up.z >= 0.0f ? 1.0f : -1.0f;
float a = -1.0f / (z_sign + up.z);
float b = up.x * up.y * a;
float3x3 basis;
basis[0] = float3(1.0f + z_sign * square(up.x) * a, z_sign * b, -z_sign * up.x);
basis[1] = float3(b, z_sign + square(up.y) * a, -up.y);
basis[2] = up;
return basis;
}
/** \} */

View File

@@ -0,0 +1,49 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_math_base_lib.glsl"
#include "gpu_shader_math_euler_lib.glsl"
#include "gpu_shader_math_matrix_compare_lib.glsl"
#include "gpu_shader_math_quaternion_lib.glsl"
#include "gpu_shader_utildefines_lib.glsl"
/* -------------------------------------------------------------------- */
/** \name Conversion function.
* \{ */
/**
* Extract the absolute 3d scale from a transform matrix.
*/
float3 to_scale(float3x3 mat)
{
return float3(length(mat[0]), length(mat[1]), length(mat[2]));
}
/**
* Extract the absolute 3d scale from a transform matrix.
*/
float3 to_scale(float4x4 mat)
{
return to_scale(to_float3x3(mat));
}
/**
* Extract the absolute 3d scale from a transform matrix.
*/
template<typename MatT, bool allow_negative_scale> float3 to_scale(MatT mat)
{
float3 result = to_scale(mat);
if (allow_negative_scale) {
if (is_negative(mat)) {
result = -result;
}
}
return result;
}
template float3 to_scale<float3x3, true>(float3x3 mat);
template float3 to_scale<float3x3, false>(float3x3 mat);
template float3 to_scale<float4x4, true>(float4x4 mat);
template float3 to_scale<float4x4, false>(float4x4 mat);
/** \} */

View File

@@ -0,0 +1,57 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_math_matrix_construct_lib.glsl"
#include "gpu_shader_math_quaternion_lib.glsl"
#include "gpu_shader_math_rotation_conversion_lib.glsl"
#include "gpu_shader_math_rotation_lib.glsl"
#include "gpu_shader_math_vector_lib.glsl"
/* -------------------------------------------------------------------- */
/** \name Interpolate
* \{ */
#if 0 /* TODO(@fclem): Implement */
/**
* Naive interpolation implementation, faster than polar decomposition
*
* \note This code is about five times faster than the polar decomposition.
* However, it gives un-expected results even with non-uniformly scaled matrices,
* see #46418 for an example.
*
* \param a: Input matrix which is totally effective with `t = 0.0`.
* \param b: Input matrix which is totally effective with `t = 1.0`.
* \param t: Interpolation factor.
*/
float3x3 interpolate_fast(float3x3 a, float3x3 b, float t);
#endif
/**
* Naive transform matrix interpolation,
* based on naive-decomposition-based interpolation from #interpolate_fast<T, 3, 3>.
*/
float4x4 interpolate_fast(float4x4 a, float4x4 b, float t)
{
float3 a_loc, b_loc;
float3 a_scale, b_scale;
Quaternion a_quat, b_quat;
to_loc_rot_scale(a, a_loc, a_quat, a_scale);
to_loc_rot_scale(b, b_loc, b_quat, b_scale);
float3 location = interpolate(a_loc, b_loc, t);
float3 scale = interpolate(a_scale, b_scale, t);
Quaternion rotation = interpolate(a_quat, b_quat, t);
return from_loc_rot_scale(location, rotation, scale);
}
/**
* Compute Moore-Penrose pseudo inverse of matrix.
* Singular values below epsilon are ignored for stability (truncated SVD).
*/
/* TODO(fclem): Implement */
// mat4x4 pseudo_invert(mat4x4 mat, float epsilon); /* Not implemented. Too complex to port. */
/** \} */

View File

@@ -0,0 +1,136 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
/* -------------------------------------------------------------------- */
/** \name Matrix Operations
* \{ */
/**
* Flip the matrix across its diagonal. Also flips dimensions for non square matrices.
*/
// float3x3 transpose(float3x3 mat); /* Built-In using shading languages. */
/**
* Returns the determinant of the matrix.
* It can be interpreted as the signed volume (or area) of the unit cube after transformation.
*/
// float determinant(float3x3 mat); /* Built-In using shading languages. */
/**
* Returns the inverse of a square matrix or zero matrix on failure.
* \a r_success is optional and set to true if the matrix was inverted successfully.
*/
float2x2 invert(float2x2 mat)
{
return inverse(mat);
}
/**
* Returns the inverse of a square matrix or zero matrix on failure.
* \a r_success is optional and set to true if the matrix was inverted successfully.
*/
float3x3 invert(float3x3 mat)
{
return inverse(mat);
}
/**
* Returns the inverse of a square matrix or zero matrix on failure.
* \a r_success is optional and set to true if the matrix was inverted successfully.
*/
float4x4 invert(float4x4 mat)
{
return inverse(mat);
}
/**
* Returns the inverse of a square matrix or zero matrix on failure.
* \a r_success is optional and set to true if the matrix was inverted successfully.
*/
float2x2 invert(float2x2 mat, bool &r_success)
{
r_success = determinant(mat) != 0.0f;
return r_success ? inverse(mat) : float2x2(0.0f);
}
/**
* Returns the inverse of a square matrix or zero matrix on failure.
* \a r_success is optional and set to true if the matrix was inverted successfully.
*/
float3x3 invert(float3x3 mat, bool &r_success)
{
r_success = determinant(mat) != 0.0f;
return r_success ? inverse(mat) : float3x3(0.0f);
}
/**
* Returns the inverse of a square matrix or zero matrix on failure.
* \a r_success is optional and set to true if the matrix was inverted successfully.
*/
float4x4 invert(float4x4 mat, bool &r_success)
{
r_success = determinant(mat) != 0.0f;
return r_success ? inverse(mat) : float4x4(0.0f);
}
/**
* Equivalent to `mat * from_location(translation)` but with fewer operation.
*/
float4x4 translate(float4x4 mat, float3 translation)
{
mat[3].xyz += translation[0] * mat[0].xyz;
mat[3].xyz += translation[1] * mat[1].xyz;
mat[3].xyz += translation[2] * mat[2].xyz;
return mat;
}
/**
* Equivalent to `mat * from_location(translation)` but with fewer operation.
*/
float4x4 translate(float4x4 mat, float2 translation)
{
mat[3].xyz += translation[0] * mat[0].xyz;
mat[3].xyz += translation[1] * mat[1].xyz;
return mat;
}
/**
* Equivalent to `mat * from_scale(scale)` but with fewer operation.
*/
float3x3 scale(float3x3 mat, float2 scale)
{
mat[0] *= scale[0];
mat[1] *= scale[1];
return mat;
}
/**
* Equivalent to `mat * from_scale(scale)` but with fewer operation.
*/
float3x3 scale(float3x3 mat, float3 scale)
{
mat[0] *= scale[0];
mat[1] *= scale[1];
mat[2] *= scale[2];
return mat;
}
/**
* Equivalent to `mat * from_scale(scale)` but with fewer operation.
*/
float4x4 scale(float4x4 mat, float2 scale)
{
mat[0] *= scale[0];
mat[1] *= scale[1];
return mat;
}
/**
* Equivalent to `mat * from_scale(scale)` but with fewer operation.
*/
float4x4 scale(float4x4 mat, float3 scale)
{
mat[0] *= scale[0];
mat[1] *= scale[1];
mat[2] *= scale[2];
return mat;
}
/** \} */

View File

@@ -0,0 +1,209 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
#include "gpu_shader_math_vector_lib.glsl"
/* -------------------------------------------------------------------- */
/** \name Normalize
* \{ */
/* Needs to be defined for correct overloading. */
#if defined(GPU_OPENGL) || defined(GPU_METAL)
float2 normalize(float2 a)
{
return a * inversesqrt(length_squared(a));
}
float3 normalize(float3 a)
{
return a * inversesqrt(length_squared(a));
}
float4 normalize(float4 a)
{
return a * inversesqrt(length_squared(a));
}
#endif
/**
* Normalize each column of the matrix individually.
*/
#if 0 /* Remove unused variants as they are slow down compilation. */
float2x2 normalize(float2x2 mat)
{
float2x2 ret;
ret[0] = normalize(mat[0].xy);
ret[1] = normalize(mat[1].xy);
return ret;
}
float2x3 normalize(float2x3 mat)
{
float2x3 ret;
ret[0] = normalize(mat[0].xyz);
ret[1] = normalize(mat[1].xyz);
return ret;
}
float2x4 normalize(float2x4 mat)
{
float2x4 ret;
ret[0] = normalize(mat[0].xyzw);
ret[1] = normalize(mat[1].xyzw);
return ret;
}
float3x2 normalize(float3x2 mat)
{
float3x2 ret;
ret[0] = normalize(mat[0].xy);
ret[1] = normalize(mat[1].xy);
ret[2] = normalize(mat[2].xy);
return ret;
}
#endif
float3x3 normalize(float3x3 mat)
{
float3x3 ret;
ret[0] = normalize(mat[0].xyz);
ret[1] = normalize(mat[1].xyz);
ret[2] = normalize(mat[2].xyz);
return ret;
}
#if 0 /* Remove unused variants as they are slow down compilation. */
float3x4 normalize(float3x4 mat)
{
float3x4 ret;
ret[0] = normalize(mat[0].xyzw);
ret[1] = normalize(mat[1].xyzw);
ret[2] = normalize(mat[2].xyzw);
return ret;
}
float4x2 normalize(float4x2 mat)
{
float4x2 ret;
ret[0] = normalize(mat[0].xy);
ret[1] = normalize(mat[1].xy);
ret[2] = normalize(mat[2].xy);
ret[3] = normalize(mat[3].xy);
return ret;
}
float4x3 normalize(float4x3 mat)
{
float4x3 ret;
ret[0] = normalize(mat[0].xyz);
ret[1] = normalize(mat[1].xyz);
ret[2] = normalize(mat[2].xyz);
ret[3] = normalize(mat[3].xyz);
return ret;
}
#endif
float4x4 normalize(float4x4 mat)
{
float4x4 ret;
ret[0] = normalize(mat[0].xyzw);
ret[1] = normalize(mat[1].xyzw);
ret[2] = normalize(mat[2].xyzw);
ret[3] = normalize(mat[3].xyzw);
return ret;
}
/**
* Normalize each column of the matrix individually.
* Return the length of each column vector.
*/
#if 0 /* Remove unused variants as they are slow down compilation. */
float2x2 normalize_and_get_size(float2x2 mat, float2 & r_size)
{
float size_x = 0.0f, size_y = 0.0f;
float2x2 ret;
ret[0] = normalize_and_get_length(mat[0], size_x);
ret[1] = normalize_and_get_length(mat[1], size_y);
r_size = float2(size_x, size_y);
return ret;
}
float2x3 normalize_and_get_size(float2x3 mat, float2 & r_size)
{
float size_x = 0.0f, size_y = 0.0f;
float2x3 ret;
ret[0] = normalize_and_get_length(mat[0], size_x);
ret[1] = normalize_and_get_length(mat[1], size_y);
r_size = float2(size_x, size_y);
return ret;
}
float2x4 normalize_and_get_size(float2x4 mat, float2 & r_size)
{
float size_x = 0.0f, size_y = 0.0f;
float2x4 ret;
ret[0] = normalize_and_get_length(mat[0], size_x);
ret[1] = normalize_and_get_length(mat[1], size_y);
r_size = float2(size_x, size_y);
return ret;
}
float3x2 normalize_and_get_size(float3x2 mat, float3 & r_size)
{
float size_x = 0.0f, size_y = 0.0f, size_z = 0.0f;
float3x2 ret;
ret[0] = normalize_and_get_length(mat[0], size_x);
ret[1] = normalize_and_get_length(mat[1], size_y);
ret[2] = normalize_and_get_length(mat[2], size_z);
r_size = float3(size_x, size_y, size_z);
return ret;
}
#endif
float3x3 normalize_and_get_size(float3x3 mat, float3 &r_size)
{
float size_x = 0.0f, size_y = 0.0f, size_z = 0.0f;
float3x3 ret;
ret[0] = normalize_and_get_length(mat[0], size_x);
ret[1] = normalize_and_get_length(mat[1], size_y);
ret[2] = normalize_and_get_length(mat[2], size_z);
r_size = float3(size_x, size_y, size_z);
return ret;
}
#if 0 /* Remove unused variants as they are slow down compilation. */
float3x4 normalize_and_get_size(float3x4 mat, float3 & r_size)
{
float size_x = 0.0f, size_y = 0.0f, size_z = 0.0f;
float3x4 ret;
ret[0] = normalize_and_get_length(mat[0], size_x);
ret[1] = normalize_and_get_length(mat[1], size_y);
ret[2] = normalize_and_get_length(mat[2], size_z);
r_size = float3(size_x, size_y, size_z);
return ret;
}
float4x2 normalize_and_get_size(float4x2 mat, float4 & r_size)
{
float size_x = 0.0f, size_y = 0.0f, size_z = 0.0f, size_w = 0.0f;
float4x2 ret;
ret[0] = normalize_and_get_length(mat[0], size_x);
ret[1] = normalize_and_get_length(mat[1], size_y);
ret[2] = normalize_and_get_length(mat[2], size_z);
ret[3] = normalize_and_get_length(mat[3], size_w);
r_size = float4(size_x, size_y, size_z, size_w);
return ret;
}
float4x3 normalize_and_get_size(float4x3 mat, float4 & r_size)
{
float size_x = 0.0f, size_y = 0.0f, size_z = 0.0f, size_w = 0.0f;
float4x3 ret;
ret[0] = normalize_and_get_length(mat[0], size_x);
ret[1] = normalize_and_get_length(mat[1], size_y);
ret[2] = normalize_and_get_length(mat[2], size_z);
ret[3] = normalize_and_get_length(mat[3], size_w);
r_size = float4(size_x, size_y, size_z, size_w);
return ret;
}
#endif
float4x4 normalize_and_get_size(float4x4 mat, float4 &r_size)
{
float size_x = 0.0f, size_y = 0.0f, size_z = 0.0f, size_w = 0.0f;
float4x4 ret;
ret[0] = normalize_and_get_length(mat[0], size_x);
ret[1] = normalize_and_get_length(mat[1], size_y);
ret[2] = normalize_and_get_length(mat[2], size_z);
ret[3] = normalize_and_get_length(mat[3], size_w);
r_size = float4(size_x, size_y, size_z, size_w);
return ret;
}
/** \} */

View File

@@ -0,0 +1,84 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
/* -------------------------------------------------------------------- */
/** \name Projection Matrices.
* \{ */
/**
* \brief Create an orthographic projection matrix using OpenGL coordinate convention:
* Maps each axis range to [-1..1] range for all axes.
* The resulting matrix can be used with either #project_point or #transform_point.
*/
float4x4 projection_orthographic(
float left, float right, float bottom, float top, float near_clip, float far_clip)
{
float x_delta = right - left;
float y_delta = top - bottom;
float z_delta = far_clip - near_clip;
float4x4 mat = float4x4(1.0f);
if (x_delta != 0.0f && y_delta != 0.0f && z_delta != 0.0f) {
mat[0][0] = 2.0f / x_delta;
mat[3][0] = -(right + left) / x_delta;
mat[1][1] = 2.0f / y_delta;
mat[3][1] = -(top + bottom) / y_delta;
mat[2][2] = -2.0f / z_delta; /* NOTE: negate Z. */
mat[3][2] = -(far_clip + near_clip) / z_delta;
}
return mat;
}
/**
* \brief Create a perspective projection matrix using OpenGL coordinate convention:
* Maps each axis range to [-1..1] range for all axes.
* `left`, `right`, `bottom`, `top` are frustum side distances at `z=near_clip`.
* The resulting matrix can be used with #project_point.
*/
float4x4 projection_perspective(
float left, float right, float bottom, float top, float near_clip, float far_clip)
{
float x_delta = right - left;
float y_delta = top - bottom;
float z_delta = far_clip - near_clip;
float4x4 mat = float4x4(1.0f);
if (x_delta != 0.0f && y_delta != 0.0f && z_delta != 0.0f) {
mat[0][0] = near_clip * 2.0f / x_delta;
mat[1][1] = near_clip * 2.0f / y_delta;
mat[2][0] = (right + left) / x_delta; /* NOTE: negate Z. */
mat[2][1] = (top + bottom) / y_delta;
mat[2][2] = -(far_clip + near_clip) / z_delta;
mat[2][3] = -1.0f;
mat[3][2] = (-2.0f * near_clip * far_clip) / z_delta;
mat[3][3] = 0.0f;
}
return mat;
}
/**
* \brief Create a perspective projection matrix using OpenGL coordinate convention:
* Maps each axis range to [-1..1] range for all axes.
* Uses field of view angles instead of plane distances.
* The resulting matrix can be used with #project_point.
*/
float4x4 projection_perspective_fov(float angle_left,
float angle_right,
float angle_bottom,
float angle_top,
float near_clip,
float far_clip)
{
float4x4 mat = projection_perspective(
tan(angle_left), tan(angle_right), tan(angle_bottom), tan(angle_top), near_clip, far_clip);
mat[0][0] /= near_clip;
mat[1][1] /= near_clip;
return mat;
}
/** \} */

View File

@@ -0,0 +1,72 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
/* -------------------------------------------------------------------- */
/** \name Transform function.
* \{ */
/**
* Transform a 3d point using a 3x3 matrix (rotation & scale).
*/
float3 transform_point(float3x3 mat, float3 point)
{
return mat * point;
}
/**
* Transform a 3d point using a 4x4 matrix (location & rotation & scale).
*/
float3 transform_point(float4x4 mat, float3 point)
{
return (mat * float4(point, 1.0f)).xyz;
}
/**
* Transform a 2d point using a 3x3 matrix (location & rotation & scale).
*/
float2 transform_point(float3x3 mat, float2 point)
{
return (mat * float3(point, 1.0f)).xy;
}
/**
* Transform a 3d direction vector using a 3x3 matrix (rotation & scale).
*/
float3 transform_direction(float3x3 mat, float3 direction)
{
return mat * direction;
}
/**
* Transform a 3d direction vector using a 4x4 matrix (rotation & scale).
*/
float3 transform_direction(float4x4 mat, float3 direction)
{
return to_float3x3(mat) * direction;
}
/**
* Project a point using a matrix (location & rotation & scale & perspective divide).
*/
float2 project_point(float3x3 mat, float2 point)
{
float3 tmp = mat * float3(point, 1.0f);
/* Absolute value to not flip the frustum upside down behind the camera. */
return tmp.xy / abs(tmp.z);
}
/**
* Project a point using a matrix (location & rotation & scale & perspective divide).
*/
float3 project_point(float4x4 mat, float3 point)
{
float4 tmp = mat * float4(point, 1.0f);
/* Absolute value to not flip the frustum upside down behind the camera. */
return tmp.xyz / abs(tmp.w);
}
/** \} */

View File

@@ -0,0 +1,50 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
struct Quaternion {
float x, y, z, w;
static Quaternion identity()
{
return {1, 0, 0, 0};
}
float4 as_float4() const
{
return float4(this->x, this->y, this->z, this->w);
}
};
/* -------------------------------------------------------------------- */
/** \name Quaternion Math
* \{ */
Quaternion math_quaternion_multiply(Quaternion a, Quaternion b)
{
Quaternion result;
result.x = a.x * b.x - a.y * b.y - a.z * b.z - a.w * b.w;
result.y = a.x * b.y + a.y * b.x + a.z * b.w - a.w * b.z;
result.z = a.x * b.z - a.y * b.w + a.z * b.x + a.w * b.y;
result.w = a.x * b.w + a.y * b.z - a.z * b.y + a.w * b.x;
return result;
}
Quaternion quaternion_conjugate(Quaternion q)
{
return {q.x, -q.y, -q.z, -q.w};
}
float3 transform_point_by_quaternion(Quaternion q, float3 v)
{
const Quaternion v_quat = {0.0f, v.x, v.y, v.z};
const Quaternion result = math_quaternion_multiply(math_quaternion_multiply(q, v_quat),
quaternion_conjugate(q));
return float3(result.y, result.z, result.w);
}
/** \} */

View File

@@ -0,0 +1,396 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_math_axis_angle_lib.glsl"
#include "gpu_shader_math_base_lib.glsl"
#include "gpu_shader_math_euler_lib.glsl"
#include "gpu_shader_math_matrix_conversion_lib.glsl"
#include "gpu_shader_math_matrix_normalize_lib.glsl"
#include "gpu_shader_math_quaternion_lib.glsl"
#include "gpu_shader_math_vector_compare_lib.glsl"
#include "gpu_shader_math_vector_lib.glsl"
#include "gpu_shader_utildefines_lib.glsl"
namespace detail {
Quaternion normalized_to_quat_fast(float3x3 mat)
{
/* Caller must ensure matrices aren't negative for valid results, see: #24291, #94231. */
Quaternion q;
/* Method outlined by Mike Day, ref: https://math.stackexchange.com/a/3183435/220949
* with an additional `sqrtf(..)` for higher precision result.
* Removing the `sqrt` causes tests to fail unless the precision is set to 1e-6f or larger. */
if (mat[2][2] < 0.0f) {
if (mat[0][0] > mat[1][1]) {
float trace = 1.0f + mat[0][0] - mat[1][1] - mat[2][2];
float s = 2.0f * sqrt(trace);
if (mat[1][2] < mat[2][1]) {
/* Ensure W is non-negative for a canonical result. */
s = -s;
}
q.y = 0.25f * s;
s = 1.0f / s;
q.x = (mat[1][2] - mat[2][1]) * s;
q.z = (mat[0][1] + mat[1][0]) * s;
q.w = (mat[2][0] + mat[0][2]) * s;
if ((trace == 1.0f) && (q.x == 0.0f && q.z == 0.0f && q.w == 0.0f)) {
/* Avoids the need to normalize the degenerate case. */
q.y = 1.0f;
}
}
else {
float trace = 1.0f - mat[0][0] + mat[1][1] - mat[2][2];
float s = 2.0f * sqrt(trace);
if (mat[2][0] < mat[0][2]) {
/* Ensure W is non-negative for a canonical result. */
s = -s;
}
q.z = 0.25f * s;
s = 1.0f / s;
q.x = (mat[2][0] - mat[0][2]) * s;
q.y = (mat[0][1] + mat[1][0]) * s;
q.w = (mat[1][2] + mat[2][1]) * s;
if ((trace == 1.0f) && (q.x == 0.0f && q.y == 0.0f && q.w == 0.0f)) {
/* Avoids the need to normalize the degenerate case. */
q.z = 1.0f;
}
}
}
else {
if (mat[0][0] < -mat[1][1]) {
float trace = 1.0f - mat[0][0] - mat[1][1] + mat[2][2];
float s = 2.0f * sqrt(trace);
if (mat[0][1] < mat[1][0]) {
/* Ensure W is non-negative for a canonical result. */
s = -s;
}
q.w = 0.25f * s;
s = 1.0f / s;
q.x = (mat[0][1] - mat[1][0]) * s;
q.y = (mat[2][0] + mat[0][2]) * s;
q.z = (mat[1][2] + mat[2][1]) * s;
if ((trace == 1.0f) && (q.x == 0.0f && q.y == 0.0f && q.z == 0.0f)) {
/* Avoids the need to normalize the degenerate case. */
q.w = 1.0f;
}
}
else {
/* NOTE(@ideasman42): A zero matrix will fall through to this block,
* needed so a zero scaled matrices to return a quaternion without rotation, see: #101848. */
float trace = 1.0f + mat[0][0] + mat[1][1] + mat[2][2];
float s = 2.0f * sqrt(trace);
q.x = 0.25f * s;
s = 1.0f / s;
q.y = (mat[1][2] - mat[2][1]) * s;
q.z = (mat[2][0] - mat[0][2]) * s;
q.w = (mat[0][1] - mat[1][0]) * s;
if ((trace == 1.0f) && (q.y == 0.0f && q.z == 0.0f && q.w == 0.0f)) {
/* Avoids the need to normalize the degenerate case. */
q.x = 1.0f;
}
}
}
return q;
}
Quaternion normalized_to_quat_with_checks(float3x3 mat)
{
float det = determinant(mat);
if (!isfinite(det)) {
return Quaternion::identity();
}
if (det < 0.0f) {
return normalized_to_quat_fast(-mat);
}
return normalized_to_quat_fast(mat);
}
void normalized_to_eul2(float3x3 mat, EulerXYZ &eul1, EulerXYZ &eul2)
{
float cy = hypot(mat[0][0], mat[0][1]);
if (cy > 16.0f * FLT_EPSILON) {
eul1.x = atan2(mat[1][2], mat[2][2]);
eul1.y = atan2(-mat[0][2], cy);
eul1.z = atan2(mat[0][1], mat[0][0]);
eul2.x = atan2(-mat[1][2], -mat[2][2]);
eul2.y = atan2(-mat[0][2], -cy);
eul2.z = atan2(-mat[0][1], -mat[0][0]);
}
else {
eul1.x = atan2(-mat[2][1], mat[1][1]);
eul1.y = atan2(-mat[0][2], cy);
eul1.z = 0.0f;
eul2 = eul1;
}
}
} // namespace detail
/* -------------------------------------------------------------------- */
/** \name Quaternion Functions
* \{ */
Quaternion to_quaternion(EulerXYZ eul)
{
float ti = eul.x * 0.5f;
float tj = eul.y * 0.5f;
float th = eul.z * 0.5f;
float ci = cos(ti);
float cj = cos(tj);
float ch = cos(th);
float si = sin(ti);
float sj = sin(tj);
float sh = sin(th);
float cc = ci * ch;
float cs = ci * sh;
float sc = si * ch;
float ss = si * sh;
Quaternion quat;
quat.x = cj * cc + sj * ss;
quat.y = cj * sc - sj * cs;
quat.z = cj * ss + sj * cc;
quat.w = cj * cs - sj * sc;
return quat;
}
/**
* Extract quaternion rotation from transform matrix.
* \note normalized is set to false by default.
*/
Quaternion to_quaternion(float3x3 mat)
{
return detail::normalized_to_quat_with_checks(normalize(mat));
}
/**
* Extract quaternion rotation from transform matrix.
* \note normalized is set to false by default.
*/
Quaternion to_quaternion(float3x3 mat, const bool normalized)
{
if (!normalized) {
mat = normalize(mat);
}
return to_quaternion(mat);
}
/**
* Extract quaternion rotation from transform matrix.
* \note normalized is set to false by default.
*/
Quaternion to_quaternion(float4x4 mat)
{
return to_quaternion(to_float3x3(mat));
}
/**
* Extract quaternion rotation from transform matrix.
* \note normalized is set to false by default.
*/
Quaternion to_quaternion(float4x4 mat, const bool normalized)
{
return to_quaternion(to_float3x3(mat), normalized);
}
Quaternion to_quaternion(AxisAngle axis_angle)
{
float angle_cos = cos(axis_angle.angle);
/** Using half angle identities: sin(angle / 2) = sqrt((1 - angle_cos) / 2) */
float sine = sqrt(0.5f - angle_cos * 0.5f);
float cosine = sqrt(0.5f + angle_cos * 0.5f);
/* TODO(fclem): Optimize. */
float angle_sin = sin(axis_angle.angle);
if (angle_sin < 0.0f) {
sine = -sine;
}
Quaternion quat;
quat.x = cosine;
quat.y = axis_angle.axis.x * sine;
quat.z = axis_angle.axis.y * sine;
quat.w = axis_angle.axis.z * sine;
return quat;
}
Quaternion to_quaternion(float3 axis, float angle)
{
if (is_zero(axis)) {
return Quaternion::identity();
}
AxisAngle aa;
aa.axis = normalize(axis);
aa.angle = angle;
return to_quaternion(aa);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Euler Functions
* \{ */
/**
* Extract euler rotation from transform matrix.
* \return the rotation with the smallest values from the potential candidates.
*/
EulerXYZ to_euler(float3x3 mat, const bool normalized)
{
if (!normalized) {
mat = normalize(mat);
}
EulerXYZ eul1, eul2;
detail::normalized_to_eul2(mat, eul1, eul2);
/* Return best, which is just the one with lowest values it in. */
return (length_manhattan(eul1.as_float3()) > length_manhattan(eul2.as_float3())) ? eul2 : eul1;
}
/**
* Extract euler rotation from transform matrix.
* \return the rotation with the smallest values from the potential candidates.
*/
EulerXYZ to_euler(float3x3 mat)
{
return to_euler(mat, true);
}
/**
* Extract euler rotation from transform matrix.
* \return the rotation with the smallest values from the potential candidates.
*/
EulerXYZ to_euler(float4x4 mat, const bool normalized)
{
return to_euler(to_float3x3(mat), normalized);
}
/**
* Extract euler rotation from transform matrix.
* \return the rotation with the smallest values from the potential candidates.
*/
EulerXYZ to_euler(float4x4 mat)
{
return to_euler(to_float3x3(mat));
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Axis Angle Functions
* \{ */
AxisAngle to_axis_angle(Quaternion quat)
{
/* Calculate angle/2, and sin(angle/2). */
float ha = acos(quat.x);
float si = sin(ha);
/* From half-angle to angle. */
float angle = ha * 2;
/* Prevent division by zero for axis conversion. */
if (abs(si) < 0.0005f) {
si = 1.0f;
}
float3 axis = float3(quat.y, quat.z, quat.w) / si;
if (is_zero(axis)) {
axis[1] = 1.0f;
}
return {axis, angle};
}
AxisAngle to_axis_angle(EulerXYZ eul)
{
/* Use quaternions as intermediate representation for now... */
return to_axis_angle(to_quaternion(eul));
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Matrices Functions
* \{ */
/**
* Decompose a matrix into location, rotation, and scale components.
* \tparam allow_negative_scale: if true, will compute determinant to know if matrix is negative.
* Rotation and scale values will be flipped if it is negative.
* This is a costly operation so it is disabled by default.
*/
void to_rot_scale(float3x3 mat, EulerXYZ &r_rotation, float3 &r_scale)
{
r_scale = to_scale(mat);
r_rotation = to_euler(mat, true);
}
/**
* Decompose a matrix into location, rotation, and scale components.
* \tparam allow_negative_scale: if true, will compute determinant to know if matrix is negative.
* Rotation and scale values will be flipped if it is negative.
* This is a costly operation so it is disabled by default.
*/
void to_rot_scale(float3x3 mat,
EulerXYZ &r_rotation,
float3 &r_scale,
const bool allow_negative_scale)
{
float3x3 normalized_mat = normalize_and_get_size(mat, r_scale);
if (allow_negative_scale) {
if (is_negative(normalized_mat)) {
normalized_mat = -normalized_mat;
r_scale = -r_scale;
}
}
r_rotation = to_euler(normalized_mat, true);
}
void to_rot_scale(float3x3 mat, Quaternion &r_rotation, float3 &r_scale)
{
r_scale = to_scale(mat);
r_rotation = to_quaternion(mat, true);
}
void to_rot_scale(float3x3 mat,
Quaternion &r_rotation,
float3 &r_scale,
const bool allow_negative_scale)
{
float3x3 normalized_mat = normalize_and_get_size(mat, r_scale);
if (allow_negative_scale) {
if (is_negative(normalized_mat)) {
normalized_mat = -normalized_mat;
r_scale = -r_scale;
}
}
r_rotation = to_quaternion(normalized_mat, true);
}
void to_loc_rot_scale(float4x4 mat, float3 &r_location, EulerXYZ &r_rotation, float3 &r_scale)
{
r_location = mat[3].xyz;
to_rot_scale(to_float3x3(mat), r_rotation, r_scale);
}
void to_loc_rot_scale(float4x4 mat,
float3 &r_location,
EulerXYZ &r_rotation,
float3 &r_scale,
const bool allow_negative_scale)
{
r_location = mat[3].xyz;
to_rot_scale(to_float3x3(mat), r_rotation, r_scale, allow_negative_scale);
}
void to_loc_rot_scale(float4x4 mat, float3 &r_location, Quaternion &r_rotation, float3 &r_scale)
{
r_location = mat[3].xyz;
to_rot_scale(to_float3x3(mat), r_rotation, r_scale);
}
void to_loc_rot_scale(float4x4 mat,
float3 &r_location,
Quaternion &r_rotation,
float3 &r_scale,
const bool allow_negative_scale)
{
r_location = mat[3].xyz;
to_rot_scale(to_float3x3(mat), r_rotation, r_scale, allow_negative_scale);
}
/** \} */

View File

@@ -0,0 +1,153 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_math_axis_angle_lib.glsl"
#include "gpu_shader_math_euler_lib.glsl"
#include "gpu_shader_math_matrix_construct_lib.glsl"
#include "gpu_shader_math_quaternion_lib.glsl"
#include "gpu_shader_utildefines_lib.glsl"
/* -------------------------------------------------------------------- */
/** \name Interpolate
* \{ */
/**
* Generic function for implementing slerp
* (quaternions and spherical vector coords).
*
* \param t: factor in [0..1]
* \param cosom: dot product from normalized vectors/quaternions.
* \return calculated weights.
*/
float2 interpolate_dot_slerp(float t, float cosom)
{
float2 w = float2(1.0f - t, t);
/* Within [-1..1] range, avoid aligned axis. */
constexpr float eps = 1e-4f;
if (abs(cosom) < 1.0f - eps) {
float omega = acos(cosom);
w = sin(w * omega) / sin(omega);
}
return w;
}
Quaternion interpolate(Quaternion a, Quaternion b, float t)
{
float4 quat = a.as_float4();
float cosom = dot(a.as_float4(), b.as_float4());
/* Rotate around shortest angle. */
if (cosom < 0.0f) {
cosom = -cosom;
quat = -quat;
}
float2 w = interpolate_dot_slerp(t, cosom);
quat = w.x * quat + w.y * b.as_float4();
return Quaternion{UNPACK4(quat)};
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Rotate
* \{ */
/**
* Equivalent to `mat * from_rotation(rotation)` but with fewer operation.
* Optimized for rotation on basis vector (i.e: AxisAngle({1, 0, 0}, 0.2f)).
*/
float3x3 rotate(float3x3 mat, AxisAngle rotation)
{
float3x3 result;
/* axis_vec is given to be normalized. */
if (rotation.axis.x == 1.0f) {
float angle_cos = cos(rotation.angle);
float angle_sin = sin(rotation.angle);
for (int c = 0; c < 3; c++) {
result[0][c] = mat[0][c];
result[1][c] = angle_cos * mat[1][c] + angle_sin * mat[2][c];
result[2][c] = -angle_sin * mat[1][c] + angle_cos * mat[2][c];
}
}
else if (rotation.axis.y == 1.0f) {
float angle_cos = cos(rotation.angle);
float angle_sin = sin(rotation.angle);
for (int c = 0; c < 3; c++) {
result[0][c] = angle_cos * mat[0][c] - angle_sin * mat[2][c];
result[1][c] = mat[1][c];
result[2][c] = angle_sin * mat[0][c] + angle_cos * mat[2][c];
}
}
else if (rotation.axis.z == 1.0f) {
float angle_cos = cos(rotation.angle);
float angle_sin = sin(rotation.angle);
for (int c = 0; c < 3; c++) {
result[0][c] = angle_cos * mat[0][c] + angle_sin * mat[1][c];
result[1][c] = -angle_sin * mat[0][c] + angle_cos * mat[1][c];
result[2][c] = mat[2][c];
}
}
else {
/* Un-optimized case. Arbitrary rotation. */
result = mat * from_rotation(rotation);
}
return result;
}
/**
* Equivalent to `mat * from_rotation(rotation)` but with fewer operation.
* Optimized for rotation on basis vector (i.e: AxisAngle({1, 0, 0}, 0.2f)).
*/
float3x3 rotate(float3x3 mat, EulerXYZ rotation)
{
AxisAngle axis_angle;
if (rotation.y == 0.0f && rotation.z == 0.0f) {
axis_angle = AxisAngle{float3(1.0f, 0.0f, 0.0f), rotation.x};
}
else if (rotation.x == 0.0f && rotation.z == 0.0f) {
axis_angle = AxisAngle{float3(0.0f, 1.0f, 0.0f), rotation.y};
}
else if (rotation.x == 0.0f && rotation.y == 0.0f) {
axis_angle = AxisAngle{float3(0.0f, 0.0f, 1.0f), rotation.z};
}
else {
/* Un-optimized case. Arbitrary rotation. */
return mat * from_rotation(rotation);
}
return rotate(mat, axis_angle);
}
/**
* Equivalent to `mat * from_rotation(rotation)` but with fewer operation.
* Optimized for rotation on basis vector (i.e: AxisAngle({1, 0, 0}, 0.2f)).
*/
float4x4 rotate(float4x4 mat, AxisAngle rotation)
{
float4x4 result = to_float4x4(rotate(to_float3x3(mat), rotation));
result[0][3] = mat[0][3];
result[1][3] = mat[1][3];
result[2][3] = mat[2][3];
result[3][0] = mat[3][0];
result[3][1] = mat[3][1];
result[3][2] = mat[3][2];
result[3][3] = mat[3][3];
return result;
}
/**
* Equivalent to `mat * from_rotation(rotation)` but with fewer operation.
* Optimized for rotation on basis vector (i.e: AxisAngle({1, 0, 0}, 0.2f)).
*/
float4x4 rotate(float4x4 mat, EulerXYZ rotation)
{
float4x4 result = to_float4x4(rotate(to_float3x3(mat), rotation));
result[0][3] = mat[0][3];
result[1][3] = mat[1][3];
result[2][3] = mat[2][3];
result[3][0] = mat[3][0];
result[3][1] = mat[3][1];
result[3][2] = mat[3][2];
result[3][3] = mat[3][3];
return result;
}
/** \} */

View File

@@ -0,0 +1,129 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
#include "gpu_shader_math_constants_lib.glsl"
/* -------------------------------------------------------------------- */
/** \name Safe Math Functions
* \{ */
/**
* Safe `a` modulo `b`.
* If `b` equal 0 the result will be 0.
*/
float safe_mod(float a, float b)
{
return (b != 0.0f) ? mod(a, b) : 0.0f;
}
/**
* Safe divide `a` by `b`.
* If `b` equal 0 the result will be 0.
*/
float safe_divide(float a, float b)
{
return (b != 0.0f) ? (a / b) : 0.0f;
}
/**
* Safe reciprocal function. Returns `1/a`.
* If `a` equal 0 the result will be 0.
*/
float safe_rcp(float a)
{
return (a != 0.0f) ? (1.0f / a) : 0.0f;
}
/**
* Safe square root function. Returns `sqrt(a)`.
* If `a` is less or equal to 0 then the result will be 0.
*/
float safe_sqrt(float a)
{
return sqrt(max(0.0f, a));
}
/**
* Safe `arccosine` function. Returns `acos(a)`.
* If `a` is greater than 1, returns 0.
* If `a` is less than -1, returns PI.
*/
float safe_acos(float a)
{
if (a <= -1.0f) {
return M_PI;
}
else if (a >= 1.0f) {
return 0.0f;
}
return acos(a);
}
/**
* A version of pow that returns a fallback value if the computation is undefined. From the spec:
* The result is undefined if x < 0 or if x = 0 and y is less than or equal 0.
*/
float fallback_pow(float x, float y, float fallback)
{
if (x < 0.0f || (x == 0.0f && y <= 0.0f)) {
return fallback;
}
return pow(x, y);
}
/**
* A version of pow that behaves similar to C++ std::pow.
*/
float compatible_pow(float x, float y)
{
if (y == 0.0f) { /* x^0 -> 1, including 0^0 */
return 1.0f;
}
/* GLSL pow doesn't accept negative x. */
if (x < 0.0f) {
if (mod(-y, 2.0f) == 0.0f) {
return pow(-x, y);
}
else {
return -pow(-x, y);
}
}
else if (x == 0.0f) {
return 0.0f;
}
return pow(x, y);
}
/**
* A version of mod that behaves similar to C++ `std::modf`, and is safe such that it returns 0
* when b is also 0.
*/
float compatible_mod(float a, float b)
{
if (b != 0.0f) {
int N = int(a / b);
return a - N * b;
}
return 0.0f;
}
/**
* Wrap the given value a to fall within the range [b, c].
*/
float wrap(float a, float b, float c)
{
float range = b - c;
/* Avoid discrepancy on some hardware due to floating point accuracy and fast math. */
float s = (a != b) ? floor((a - c) / range) : 1.0f;
return (range != 0.0f) ? a - range * s : c;
}
/** \} */

View File

@@ -0,0 +1,78 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
/* ---------------------------------------------------------------------- */
/** \name Comparison
* \{ */
/**
* Return true if all components is equal to zero.
*/
template<typename VecT> bool is_zero(VecT vec)
{
return all(equal(vec, VecT(0.0f)));
}
template bool is_zero<float2>(float2);
template bool is_zero<float3>(float3);
template bool is_zero<float4>(float4);
/**
* Return true if any component is equal to zero.
*/
template<typename VecT> bool is_any_zero(VecT vec)
{
return any(equal(vec, VecT(0.0f)));
}
template bool is_any_zero<float2>(float2);
template bool is_any_zero<float3>(float3);
template bool is_any_zero<float4>(float4);
/**
* Return true if the difference between`a` and `b` is below the `epsilon` value.
*/
template<typename VecT> bool is_equal(VecT a, VecT b, float epsilon)
{
return all(lessThanEqual(abs(a - b), VecT(epsilon)));
}
template bool is_equal<float2>(float2, float2, float);
template bool is_equal<float3>(float3, float3, float);
template bool is_equal<float4>(float4, float4, float);
/**
* Return true if the deference between`a` and `b` is below the `epsilon` value.
* Epsilon value is scaled by magnitude of `a` before comparison.
*/
template<typename VecT, int dim>
bool almost_equal_relative(VecT a, VecT b, const float epsilon_factor)
{
for (int i = 0; i < dim; i++) {
if (abs(a[i] - b[i]) > epsilon_factor * abs(a[i])) {
return false;
}
}
return true;
}
template bool almost_equal_relative<float2, 2>(float2 a, float2 b, const float epsilon_factor);
template bool almost_equal_relative<float3, 3>(float3 a, float3 b, const float epsilon_factor);
template bool almost_equal_relative<float4, 4>(float4 a, float4 b, const float epsilon_factor);
/* Checks are flipped so NAN doesn't assert because we're making sure the value was
* normalized and in the case we don't want NAN to be raising asserts since there
* is nothing to be done in that case. */
template<typename VecT> bool is_unit_scale(VecT v)
{
constexpr float assert_unit_epsilon = 0.0002f;
float test_unit = dot(v, v);
return (!(abs(test_unit - 1.0f) >= assert_unit_epsilon) ||
!(abs(test_unit) >= assert_unit_epsilon));
}
template bool is_unit_scale<float2>(float2);
template bool is_unit_scale<float3>(float3);
template bool is_unit_scale<float4>(float4);
/** \} */

View File

@@ -0,0 +1,179 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
/**
* Returns \a a if it is a multiple of \a b or the next multiple or \a b after \b a .
* In other words, it is equivalent to `divide_ceil(a, b) * b`.
* It is undefined if \a a is negative or \b b is not strictly positive.
*/
template<typename VecT> VecT ceil_to_multiple(VecT a, VecT b)
{
return ((a + b - VecT(1)) / b) * b;
}
template int2 ceil_to_multiple<int2>(int2, int2);
template int3 ceil_to_multiple<int3>(int3, int3);
template int4 ceil_to_multiple<int4>(int4, int4);
template uint2 ceil_to_multiple<uint2>(uint2, uint2);
template uint3 ceil_to_multiple<uint3>(uint3, uint3);
template uint4 ceil_to_multiple<uint4>(uint4, uint4);
/**
* Integer division that returns the ceiling, instead of flooring like normal C division.
* It is undefined if \a a is negative or \b b is not strictly positive.
*/
template<typename VecT> VecT divide_ceil(VecT a, VecT b)
{
return (a + b - VecT(1)) / b;
}
template int2 divide_ceil<int2>(int2, int2);
template int3 divide_ceil<int3>(int3, int3);
template int4 divide_ceil<int4>(int4, int4);
template uint2 divide_ceil<uint2>(uint2, uint2);
template uint3 divide_ceil<uint3>(uint3, uint3);
template uint4 divide_ceil<uint4>(uint4, uint4);
/**
* Component wise, use vector to replace min if it is smaller and max if bigger.
*/
template<typename VecT> void min_max(VecT vector, VecT &min_v, VecT &max_v)
{
min_v = min(vector, min_v);
max_v = max(vector, max_v);
}
template void min_max<float2>(float2, float2 &, float2 &);
template void min_max<float3>(float3, float3 &, float3 &);
template void min_max<float4>(float4, float4 &, float4 &);
/**
* Return the manhattan length of `a`.
* This is also the sum of the absolute value of all components.
*/
template<typename VecT> float length_manhattan(VecT a)
{
return dot(abs(a), VecT(1));
}
template float length_manhattan<float2>(float2);
template float length_manhattan<float3>(float3);
template float length_manhattan<float4>(float4);
/**
* Return the length squared of `a`.
*/
template<typename VecT> float length_squared(VecT a)
{
return dot(a, a);
}
template float length_squared<float2>(float2);
template float length_squared<float3>(float3);
template float length_squared<float4>(float4);
/**
* Return the manhattan distance between `a` and `b`.
*/
template<typename VecT> float distance_manhattan(VecT a, VecT b)
{
return length_manhattan(a - b);
}
template float distance_manhattan<float2>(float2, float2);
template float distance_manhattan<float3>(float3, float3);
template float distance_manhattan<float4>(float4, float4);
/**
* Return the squared distance between `a` and `b`.
*/
template<typename VecT> float distance_squared(VecT a, VecT b)
{
return length_squared(a - b);
}
template float distance_squared<float2>(float2, float2);
template float distance_squared<float3>(float3, float3);
template float distance_squared<float4>(float4, float4);
/**
* Return normalized version of the `vector` and its length.
*/
template<typename VecT> VecT normalize_and_get_length(VecT vector, float &out_length)
{
out_length = length_squared(vector);
constexpr float threshold = 1e-35f;
if (out_length > threshold) {
out_length = sqrt(out_length);
return vector / out_length;
}
/* Either the vector is small or one of its values contained `nan`. */
out_length = 0.0f;
return VecT(0.0f);
}
template float2 normalize_and_get_length<float2>(float2, float &);
template float3 normalize_and_get_length<float3>(float3, float &);
template float4 normalize_and_get_length<float4>(float4, float &);
/**
* Per component linear interpolation.
*/
template<typename VecT> VecT interpolate(VecT a, VecT b, float t)
{
return mix(a, b, t);
}
template float2 interpolate<float2>(float2, float2, float);
template float3 interpolate<float3>(float3, float3, float);
template float4 interpolate<float4>(float4, float4, float);
/**
* Return half-way point between `a` and `b`.
*/
template<typename VecT> VecT midpoint(VecT a, VecT b)
{
return (a + b) * 0.5f;
}
template float2 midpoint<float2>(float2, float2);
template float3 midpoint<float3>(float3, float3);
template float4 midpoint<float4>(float4, float4);
/**
* Return the index of the component with the greatest absolute value.
*/
int dominant_axis(float3 a)
{
float3 b = abs(a);
return ((b.x > b.y) ? ((b.x > b.z) ? 0 : 2) : ((b.y > b.z) ? 1 : 2));
}
/**
* Calculates a perpendicular vector to \a v.
* \note Returned vector is always rotated 90 degrees counter clock wise.
*/
template<typename VecT> VecT orthogonal(VecT v)
{
return VecT(-v.y, v.x);
}
template int2 orthogonal<int2>(int2);
template float2 orthogonal<float2>(float2);
/**
* Calculates a perpendicular vector to \a v.
* \note Returned vector can be in any perpendicular direction.
* \note Returned vector might not the same length as \a v.
*/
template<> float3 orthogonal<float3>(float3 v)
{
switch (dominant_axis(v)) {
default:
case 0:
return float3(-v.y - v.z, v.x, v.x);
case 1:
return float3(v.y, -v.x - v.z, v.y);
case 2:
return float3(v.z, v.z, -v.x - v.y);
}
}
/**
* Return `vector` if `incident` and `reference` are pointing in the same direction.
*/
// float2 faceforward(float2 vector, float2 incident, float2 reference); /* Built-in GLSL. */

View File

@@ -0,0 +1,128 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
/* ---------------------------------------------------------------------- */
/** \name Comparison
* \{ */
float reduce_max(float2 a)
{
return max(a.x, a.y);
}
float reduce_max(float3 a)
{
return max(a.x, max(a.y, a.z));
}
float reduce_max(float4 a)
{
return max(max(a.x, a.y), max(a.z, a.w));
}
int reduce_max(int2 a)
{
return max(a.x, a.y);
}
int reduce_max(int3 a)
{
return max(a.x, max(a.y, a.z));
}
int reduce_max(int4 a)
{
return max(max(a.x, a.y), max(a.z, a.w));
}
float reduce_min(float2 a)
{
return min(a.x, a.y);
}
float reduce_min(float3 a)
{
return min(a.x, min(a.y, a.z));
}
float reduce_min(float4 a)
{
return min(min(a.x, a.y), min(a.z, a.w));
}
int reduce_min(int2 a)
{
return min(a.x, a.y);
}
int reduce_min(int3 a)
{
return min(a.x, min(a.y, a.z));
}
int reduce_min(int4 a)
{
return min(min(a.x, a.y), min(a.z, a.w));
}
float reduce_add(float2 a)
{
return a.x + a.y;
}
float reduce_add(float3 a)
{
return a.x + a.y + a.z;
}
float reduce_add(float4 a)
{
return a.x + a.y + a.z + a.w;
}
int reduce_add(int2 a)
{
return a.x + a.y;
}
int reduce_add(int3 a)
{
return a.x + a.y + a.z;
}
int reduce_add(int4 a)
{
return a.x + a.y + a.z + a.w;
}
#if 0 /* Remove unused variants as they are slow down compilation. */
float reduce_mul(float2 a)
{
return a.x * a.y;
}
float reduce_mul(float3 a)
{
return a.x * a.y * a.z;
}
float reduce_mul(float4 a)
{
return a.x * a.y * a.z * a.w;
}
int reduce_mul(int2 a)
{
return a.x * a.y;
}
int reduce_mul(int3 a)
{
return a.x * a.y * a.z;
}
int reduce_mul(int4 a)
{
return a.x * a.y * a.z * a.w;
}
#endif
float average(float2 a)
{
return reduce_add(a) * (1.0f / 2.0f);
}
float average(float3 a)
{
return reduce_add(a) * (1.0f / 3.0f);
}
float average(float4 a)
{
return reduce_add(a) * (1.0f / 4.0f);
}
/** \} */

View File

@@ -0,0 +1,246 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_math_safe_lib.glsl"
/**
* Safe `a` modulo `b`.
* If `b` equal 0 the result will be 0.
*/
template<typename VecT> VecT safe_mod(VecT a, VecT b)
{
return select(VecT(0), mod(a, b), notEqual(b, VecT(0)));
}
template float2 safe_mod<float2>(float2, float2);
template float3 safe_mod<float3>(float3, float3);
template float4 safe_mod<float4>(float4, float4);
/**
* Safe `a` modulo `b`.
* If `b` equal 0 the result will be 0.
*/
float2 safe_mod(float2 a, float b)
{
return (b != 0.0f) ? mod(a, float2(b)) : float2(0);
}
float3 safe_mod(float3 a, float b)
{
return (b != 0.0f) ? mod(a, float3(b)) : float3(0);
}
float4 safe_mod(float4 a, float b)
{
return (b != 0.0f) ? mod(a, float4(b)) : float4(0);
}
/**
* A version of mod that behaves similar to C++ `std::modf`, and is safe such that it returns 0
* when b is also 0.
*/
float2 compatible_mod(float2 a, float b)
{
return float2(compatible_mod(a.x, b), compatible_mod(a.y, b));
}
/**
* A version of mod that behaves similar to C++ `std::modf`, and is safe such that it returns 0
* when b is also 0.
*/
float3 compatible_mod(float3 a, float b)
{
return float3(compatible_mod(a.x, b), compatible_mod(a.y, b), compatible_mod(a.z, b));
}
/**
* A version of mod that behaves similar to C++ `std::modf`, and is safe such that it returns 0
* when b is also 0.
*/
float4 compatible_mod(float4 a, float b)
{
return float4(compatible_mod(a.x, b),
compatible_mod(a.y, b),
compatible_mod(a.z, b),
compatible_mod(a.w, b));
}
/**
* A version of mod that behaves similar to C++ `std::modf`, and is safe such that it returns 0
* when b is also 0.
*/
float2 compatible_mod(float2 a, float2 b)
{
return float2(compatible_mod(a.x, b.x), compatible_mod(a.y, b.y));
}
/**
* A version of mod that behaves similar to C++ `std::modf`, and is safe such that it returns 0
* when b is also 0.
*/
float3 compatible_mod(float3 a, float3 b)
{
return float3(compatible_mod(a.x, b.x), compatible_mod(a.y, b.y), compatible_mod(a.z, b.z));
}
/**
* A version of mod that behaves similar to C++ `std::modf`, and is safe such that it returns 0
* when b is also 0.
*/
float4 compatible_mod(float4 a, float4 b)
{
return float4(compatible_mod(a.x, b.x),
compatible_mod(a.y, b.y),
compatible_mod(a.z, b.z),
compatible_mod(a.w, b.w));
}
/**
* Safe divide `a` by `b`.
* If `b` equal 0 the result will be 0.
*/
template<typename VecT> VecT safe_divide(VecT a, VecT b)
{
return select(VecT(0), a / b, notEqual(b, VecT(0)));
}
template float2 safe_divide<float2>(float2, float2);
template float3 safe_divide<float3>(float3, float3);
template float4 safe_divide<float4>(float4, float4);
/* NOTE: Cannot overload templates. */
/**
* Safe divide `a` by `b`.
* If `b` equal 0 the result will be 0.
*/
float2 safe_divide(float2 a, float b)
{
return (b != 0.0f) ? (a / b) : float2(0);
}
float3 safe_divide(float3 a, float b)
{
return (b != 0.0f) ? (a / b) : float3(0);
}
float4 safe_divide(float4 a, float b)
{
return (b != 0.0f) ? (a / b) : float4(0);
}
/**
* Return normalized version of the `vector` or a default normalized vector if `vector` is invalid.
*/
template<typename VecT> VecT safe_normalize_and_get_length(VecT vector, float &out_length)
{
float length_squared = dot(vector, vector);
constexpr float threshold = 1e-35f;
if (length_squared > threshold) {
out_length = sqrt(length_squared);
return vector / out_length;
}
/* Either the vector is small or one of its values contained `nan`. */
out_length = 0.0f;
VecT result = VecT(0.0f);
result[0] = 1.0f;
return result;
}
template float2 safe_normalize_and_get_length<float2>(float2, float &);
template float3 safe_normalize_and_get_length<float3>(float3, float &);
template float4 safe_normalize_and_get_length<float4>(float4, float &);
/**
* Return normalized version of the `vector` or a default normalized vector if `vector` is invalid.
*/
template<typename VecT> VecT safe_normalize(VecT vector)
{
float unused_length = 0.0f;
return safe_normalize_and_get_length(vector, unused_length);
}
template float2 safe_normalize<float2>(float2);
template float3 safe_normalize<float3>(float3);
template float4 safe_normalize<float4>(float4);
/**
* Return normalized version of the `vector` or `fallback` vector if `vector` is invalid.
*/
template<typename VecT> VecT normalize_fallback(VecT vector, VecT fallback)
{
float length_squared = dot(vector, vector);
constexpr float threshold = 1e-35f;
if (length_squared > threshold) {
return vector * inversesqrt(length_squared);
}
/* Either the vector is small or one of its values contained `nan`. */
return fallback;
}
template float2 normalize_fallback<float2>(float2, float2);
template float3 normalize_fallback<float3>(float3, float3);
template float4 normalize_fallback<float4>(float4, float4);
/**
* Safe reciprocal function. Returns `1/a`.
* If `a` equal 0 the result will be 0.
*/
template<typename VecT> VecT safe_rcp(VecT a)
{
return select(VecT(0.0f), (1.0f / a), notEqual(a, VecT(0.0f)));
}
template float2 safe_rcp<float2>(float2);
template float3 safe_rcp<float3>(float3);
template float4 safe_rcp<float4>(float4);
/**
* A version of pow that returns a fallback value if the computation is undefined. From the spec:
* The result is undefined if x < 0 or if x = 0 and y is less than or equal 0.
*/
float2 fallback_pow(float2 a, float b, float2 fallback)
{
return float2(fallback_pow(a.x, b, fallback.x), fallback_pow(a.y, b, fallback.y));
}
float3 fallback_pow(float3 a, float b, float3 fallback)
{
return float3(fallback_pow(a.x, b, fallback.x),
fallback_pow(a.y, b, fallback.y),
fallback_pow(a.z, b, fallback.z));
}
float4 fallback_pow(float4 a, float b, float4 fallback)
{
return float4(fallback_pow(a.x, b, fallback.x),
fallback_pow(a.y, b, fallback.y),
fallback_pow(a.z, b, fallback.z),
fallback_pow(a.w, b, fallback.w));
}
float2 compatible_pow(float2 a, float2 b)
{
return float2(compatible_pow(a.x, b.x), compatible_pow(a.y, b.y));
}
float3 compatible_pow(float3 a, float3 b)
{
return float3(compatible_pow(a.x, b.x), compatible_pow(a.y, b.y), compatible_pow(a.z, b.z));
}
float4 compatible_pow(float4 a, float4 b)
{
return float4(compatible_pow(a.x, b.x),
compatible_pow(a.y, b.y),
compatible_pow(a.z, b.z),
compatible_pow(a.w, b.w));
}
/**
* Wrap the given value a to fall within the range [b, c].
*/
float2 wrap(float2 a, float2 b, float2 c)
{
return float2(wrap(a.x, b.x, c.x), wrap(a.y, b.y, c.y));
}
/**
* Wrap the given value a to fall within the range [b, c].
*/
float3 wrap(float3 a, float3 b, float3 c)
{
return float3(wrap(a.x, b.x, c.x), wrap(a.y, b.y, c.y), wrap(a.z, b.z, c.z));
}
/**
* Wrap the given value a to fall within the range [b, c].
*/
float4 wrap(float4 a, float4 b, float4 c)
{
return float4(
wrap(a.x, b.x, c.x), wrap(a.y, b.y, c.y), wrap(a.z, b.z, c.z), wrap(a.w, b.w, c.w));
}

View File

@@ -0,0 +1,33 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
#include "gpu_shader_index_range_lib.glsl"
/**
* See `OffsetIndices` C++ definition for formal definition.
*
* OffsetIndices cannot be implemented on GPU because of the lack of operator overloading and
* buffer reference in GLSL. So we simply interpret a given integer buffer as a `OffsetIndices`
* buffer and load a specific item as a range.
*/
namespace offset_indices {
#ifdef GLSL_CPP_STUBS
/* Equivalent of `IndexRange OffsetIndices<int>operator[]`.
* Implementation for C++ compilation. */
inline static IndexRange load_range_from_buffer(const int (&buf)[], int i)
{
return IndexRange::from_begin_end(buf[i], buf[i + 1]);
}
#endif
} // namespace offset_indices
/* Shader implementation because of missing buffer reference as argument in GLSL. */
#define offset_indices_load_range_from_buffer(buf_, i_) \
IndexRange::from_begin_end(buf_[i_], buf_[i_ + 1])

View File

@@ -0,0 +1,38 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "infos/gpu_shader_print_infos.hh"
SHADER_LIBRARY_CREATE_INFO(gpu_print)
uint print_data(uint offset, uint data)
{
if (offset < GPU_SHADER_PRINTF_MAX_CAPACITY) {
gpu_print_buf[offset] = data;
}
return offset + 1u;
}
uint print_data(uint offset, string_t data)
{
return print_data(offset, as_uint(data));
}
uint print_data(uint offset, int data)
{
return print_data(offset, uint(data));
}
uint print_data(uint offset, float data)
{
return print_data(offset, floatBitsToUint(data));
}
uint print_start(const uint data_len)
{
/* Add one to skip the length stored in the first element of the buffer. */
return atomicAdd(gpu_print_buf[0], data_len) + 1u;
}

View File

@@ -0,0 +1,14 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
/* General purpose 3D ray. */
struct Ray {
packed_float3 direction;
float max_time;
packed_float3 origin;
};

View File

@@ -0,0 +1,33 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
/**
* Ray offset to avoid self intersection.
*
* This can be used to compute a modified ray start position for rays leaving from a surface.
* From:
* "A Fast and Robust Method for Avoiding Self-Intersection"
* Ray Tracing Gems, chapter 6.
*/
float3 offset_ray(float3 P, float3 Ng)
{
constexpr float origin = 1.0f / 32.0f;
constexpr float float_scale = 1.0f / 65536.0f;
constexpr float int_scale = 256.0f;
int3 of_i = int3(int_scale * Ng);
of_i = int3((P.x < 0.0f) ? -of_i.x : of_i.x,
(P.y < 0.0f) ? -of_i.y : of_i.y,
(P.z < 0.0f) ? -of_i.z : of_i.z);
float3 P_i = intBitsToFloat(floatBitsToInt(P) + of_i);
float3 uf = P + float_scale * Ng;
return float3((abs(P.x) < origin) ? uf.x : P_i.x,
(abs(P.y) < origin) ? uf.y : P_i.y,
(abs(P.z) < origin) ? uf.z : P_i.z);
}

View File

@@ -0,0 +1,45 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "infos/gpu_shader_sequencer_infos.hh"
SHADER_LIBRARY_CREATE_INFO(gpu_shader_sequencer_strips)
/* Signed distance to rounded box, centered at origin.
* Reference: https://iquilezles.org/articles/distfunctions2d/ */
float sdf_rounded_box(float2 pos, float2 size, float radius)
{
float2 q = abs(pos) - size + radius;
return min(max(q.x, q.y), 0.0f) + length(max(q, 0.0f)) - radius;
}
void strip_box(float left,
float right,
float bottom,
float top,
float2 pos,
float2 &r_pos1,
float2 &r_pos2,
float2 &r_size,
float2 &r_center,
float2 &r_pos,
float &r_radius)
{
/* Snap to pixel grid coordinates, so that outline/border is non-fractional
* pixel sizes. */
r_pos1 = round(float2(left, bottom));
r_pos2 = round(float2(right, top));
/* Make sure strip is at least 1px wide. */
r_pos2.x = max(r_pos2.x, r_pos1.x + 1.0f);
r_size = (r_pos2 - r_pos1) * 0.5f;
r_center = (r_pos1 + r_pos2) * 0.5f;
r_pos = round(pos);
r_radius = context_data.round_radius;
if (r_radius > r_size.x) {
r_radius = 0.0f;
}
}

View File

@@ -0,0 +1,82 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
/**
* Software implementation of encoding and decoding of shared exponent texture as described by the
* OpenGL extension EXT_texture_shared_exponent Appendix
* https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_shared_exponent.txt
*
* This allows to read and write the RGB9_E5 format in a R32UI texture without explicit support on
* the hardware for this type. However, filtering is not supported in this case.
*/
#define RGB9E5_EXPONENT_BITS 5
#define RGB9E5_MANTISSA_BITS 9
#define RGB9E5_EXP_BIAS 15
#define RGB9E5_MAX_VALID_BIASED_EXP 31
#define MAX_RGB9E5_EXP (RGB9E5_MAX_VALID_BIASED_EXP - RGB9E5_EXP_BIAS)
#define RGB9E5_MANTISSA_VALUES (1 << RGB9E5_MANTISSA_BITS)
#define MAX_RGB9E5_MANTISSA (RGB9E5_MANTISSA_VALUES - 1)
int rgb9e5_floor_log2(float x)
{
/* Ok, rgb9e5_floor_log2 is not correct for the denorm and zero values, but we
* are going to do a max of this value with the minimum rgb9e5 exponent
* that will hide these problem cases. */
int biased_exponent = floatBitsToInt(x) >> 23;
return biased_exponent - 127;
}
float rgb9e5_exponent_factor(int exponent)
{
/* This pow function could be replaced by a table. There is only 32 values. */
return exp2(float(exponent - RGB9E5_EXP_BIAS - RGB9E5_MANTISSA_BITS));
}
struct rgb9e5_t {
uint exp_shared;
uint3 mantissa;
};
rgb9e5_t rgb9e5_from_float3(float3 color)
{
constexpr float max_rgb9e5 = float(0xFF80u);
color = clamp(color, 0.0f, max_rgb9e5);
float max_component = max(max(color.r, color.g), color.b);
int log2_floored = rgb9e5_floor_log2(max_component);
int exp_shared = max(-RGB9E5_EXP_BIAS - 1, log2_floored) + (1 + RGB9E5_EXP_BIAS);
float denom = rgb9e5_exponent_factor(exp_shared);
int maxm = int(max_component / denom + 0.5f);
if (maxm == MAX_RGB9E5_MANTISSA + 1) {
denom *= 2.0f;
exp_shared += 1;
}
rgb9e5_t result;
result.exp_shared = uint(exp_shared);
result.mantissa = uint3(color / denom + 0.5f);
return result;
}
uint rgb9e5_encode(float3 color)
{
rgb9e5_t result = rgb9e5_from_float3(color);
result.exp_shared <<= RGB9E5_MANTISSA_BITS * 3;
result.mantissa <<= RGB9E5_MANTISSA_BITS * uint3(0, 1, 2);
return result.mantissa.r | result.mantissa.g | result.mantissa.b | result.exp_shared;
}
float3 rgb9e5_decode(uint data)
{
int exp_shared = int(data >> (RGB9E5_MANTISSA_BITS * 3));
uint3 mantissa = (uint3(data) >> (RGB9E5_MANTISSA_BITS * uint3(0, 1, 2))) &
uint(MAX_RGB9E5_MANTISSA);
return float3(mantissa) * rgb9e5_exponent_factor(exp_shared);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,182 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
#include "gpu_shader_math_base_lib.glsl"
#include "gpu_shader_math_vector_compare_lib.glsl"
#include "GPU_shader_shared.hh"
/* clang-format off */
#ifndef GPU_METAL
bool is_integer(bool /*v*/) { return true; }
#endif
bool is_integer(uint /*v*/) { return true; }
bool is_integer(int /*v*/) { return true; }
bool is_integer(float /*v*/) { return false; }
bool is_integer(int2 /*v*/) { return true; }
bool is_integer(int3 /*v*/) { return true; }
bool is_integer(int4 /*v*/) { return true; }
bool is_integer(uint2 /*v*/) { return true; }
bool is_integer(uint3 /*v*/) { return true; }
bool is_integer(uint4 /*v*/) { return true; }
bool is_integer(float2 /*v*/) { return false; }
bool is_integer(float3 /*v*/) { return false; }
bool is_integer(float4 /*v*/) { return false; }
bool is_integer(float2x2 /*v*/) { return false; }
bool is_integer(float2x3 /*v*/) { return false; }
bool is_integer(float2x4 /*v*/) { return false; }
bool is_integer(float3x2 /*v*/) { return false; }
bool is_integer(float3x3 /*v*/) { return false; }
bool is_integer(float3x4 /*v*/) { return false; }
bool is_integer(float4x2 /*v*/) { return false; }
bool is_integer(float4x3 /*v*/) { return false; }
bool is_integer(float4x4 /*v*/) { return false; }
int mat_row_len(float2x2 /*v*/) { return 2; }
int mat_row_len(float2x3 /*v*/) { return 3; }
int mat_row_len(float2x4 /*v*/) { return 4; }
int mat_row_len(float3x2 /*v*/) { return 2; }
int mat_row_len(float3x3 /*v*/) { return 3; }
int mat_row_len(float3x4 /*v*/) { return 4; }
int mat_row_len(float4x2 /*v*/) { return 2; }
int mat_row_len(float4x3 /*v*/) { return 3; }
int mat_row_len(float4x4 /*v*/) { return 4; }
int mat_col_len(float2x2 /*v*/) { return 2; }
int mat_col_len(float2x3 /*v*/) { return 2; }
int mat_col_len(float2x4 /*v*/) { return 2; }
int mat_col_len(float3x2 /*v*/) { return 3; }
int mat_col_len(float3x3 /*v*/) { return 3; }
int mat_col_len(float3x4 /*v*/) { return 3; }
int mat_col_len(float4x2 /*v*/) { return 4; }
int mat_col_len(float4x3 /*v*/) { return 4; }
int mat_col_len(float4x4 /*v*/) { return 4; }
int mat_col_len(int2 /*v*/) { return 2; }
int mat_col_len(int3 /*v*/) { return 3; }
int mat_col_len(int4 /*v*/) { return 4; }
int mat_col_len(uint2 /*v*/) { return 2; }
int mat_col_len(uint3 /*v*/) { return 3; }
int mat_col_len(uint4 /*v*/) { return 4; }
int mat_col_len(float2 /*v*/) { return 2; }
int mat_col_len(float3 /*v*/) { return 3; }
int mat_col_len(float4 /*v*/) { return 4; }
#ifndef GPU_METAL
uint to_type(bool /*v*/) { return TEST_TYPE_BOOL; }
#endif
uint to_type(uint /*v*/) { return TEST_TYPE_UINT; }
uint to_type(int /*v*/) { return TEST_TYPE_INT; }
uint to_type(float /*v*/) { return TEST_TYPE_FLOAT; }
uint to_type(int2 /*v*/) { return TEST_TYPE_IVEC2; }
uint to_type(int3 /*v*/) { return TEST_TYPE_IVEC3; }
uint to_type(int4 /*v*/) { return TEST_TYPE_IVEC4; }
uint to_type(uint2 /*v*/) { return TEST_TYPE_UVEC2; }
uint to_type(uint3 /*v*/) { return TEST_TYPE_UVEC3; }
uint to_type(uint4 /*v*/) { return TEST_TYPE_UVEC4; }
uint to_type(float2 /*v*/) { return TEST_TYPE_VEC2; }
uint to_type(float3 /*v*/) { return TEST_TYPE_VEC3; }
uint to_type(float4 /*v*/) { return TEST_TYPE_VEC4; }
uint to_type(float2x2 /*v*/) { return TEST_TYPE_MAT2X2; }
uint to_type(float2x3 /*v*/) { return TEST_TYPE_MAT2X3; }
uint to_type(float2x4 /*v*/) { return TEST_TYPE_MAT2X4; }
uint to_type(float3x2 /*v*/) { return TEST_TYPE_MAT3X2; }
uint to_type(float3x3 /*v*/) { return TEST_TYPE_MAT3X3; }
uint to_type(float3x4 /*v*/) { return TEST_TYPE_MAT3X4; }
uint to_type(float4x2 /*v*/) { return TEST_TYPE_MAT4X2; }
uint to_type(float4x3 /*v*/) { return TEST_TYPE_MAT4X3; }
uint to_type(float4x4 /*v*/) { return TEST_TYPE_MAT4X4; }
/* clang-format on */
#define WRITE_MATRIX(v) \
TestOutputRawData raw; \
for (int c = 0; c < mat_col_len(v); c++) { \
for (int r = 0; r < mat_row_len(v); r++) { \
raw.data[c * mat_row_len(v) + r] = floatBitsToUint(v[c][r]); \
} \
} \
return raw;
#define WRITE_FLOAT_VECTOR(v) \
TestOutputRawData raw; \
for (int c = 0; c < mat_col_len(v); c++) { \
raw.data[c] = floatBitsToUint(v[c]); \
} \
return raw;
#define WRITE_INT_VECTOR(v) \
TestOutputRawData raw; \
for (int c = 0; c < mat_col_len(v); c++) { \
raw.data[c] = uint(v[c]); \
} \
return raw;
#define WRITE_FLOAT_SCALAR(v) \
TestOutputRawData raw; \
raw.data[0] = floatBitsToUint(v); \
return raw;
#define WRITE_INT_SCALAR(v) \
TestOutputRawData raw; \
raw.data[0] = uint(v); \
return raw;
/* clang-format off */
#ifndef GPU_METAL
TestOutputRawData as_raw_data(bool v) { WRITE_INT_SCALAR(v); }
#endif
TestOutputRawData as_raw_data(uint v) { WRITE_INT_SCALAR(v); }
TestOutputRawData as_raw_data(int v) { WRITE_INT_SCALAR(v); }
TestOutputRawData as_raw_data(float v) { WRITE_FLOAT_SCALAR(v); }
TestOutputRawData as_raw_data(int2 v) { WRITE_INT_VECTOR(v); }
TestOutputRawData as_raw_data(int3 v) { WRITE_INT_VECTOR(v); }
TestOutputRawData as_raw_data(int4 v) { WRITE_INT_VECTOR(v); }
TestOutputRawData as_raw_data(uint2 v) { WRITE_INT_VECTOR(v); }
TestOutputRawData as_raw_data(uint3 v) { WRITE_INT_VECTOR(v); }
TestOutputRawData as_raw_data(uint4 v) { WRITE_INT_VECTOR(v); }
TestOutputRawData as_raw_data(float2 v) { WRITE_FLOAT_VECTOR(v); }
TestOutputRawData as_raw_data(float3 v) { WRITE_FLOAT_VECTOR(v); }
TestOutputRawData as_raw_data(float4 v) { WRITE_FLOAT_VECTOR(v); }
TestOutputRawData as_raw_data(float2x2 v) { WRITE_MATRIX(v); }
TestOutputRawData as_raw_data(float2x3 v) { WRITE_MATRIX(v); }
TestOutputRawData as_raw_data(float2x4 v) { WRITE_MATRIX(v); }
TestOutputRawData as_raw_data(float3x2 v) { WRITE_MATRIX(v); }
TestOutputRawData as_raw_data(float3x3 v) { WRITE_MATRIX(v); }
TestOutputRawData as_raw_data(float3x4 v) { WRITE_MATRIX(v); }
TestOutputRawData as_raw_data(float4x2 v) { WRITE_MATRIX(v); }
TestOutputRawData as_raw_data(float4x3 v) { WRITE_MATRIX(v); }
TestOutputRawData as_raw_data(float4x4 v) { WRITE_MATRIX(v); }
/* clang-format on */
#ifdef GPU_METAL
/* Vector comparison in MSL return a `bvec`. Collapse it like in GLSL. */
# define COLLAPSE_BOOL(OP) bool(all(OP))
#else
# undef COLLAPSE_BOOL /* Silence warning caused by define grepping in info files. */
# define COLLAPSE_BOOL(OP) (OP)
#endif
#define EXPECT_OP(OP, val1, val2) \
test_output(as_raw_data(val1), as_raw_data(val2), COLLAPSE_BOOL(OP), to_type(val1))
#define EXPECT_EQ(result, expect) EXPECT_OP((result) == (expect), result, expect)
#define EXPECT_NE(result, expect) EXPECT_OP((result) != (expect), result, expect)
#define EXPECT_LE(result, expect) EXPECT_OP((result) <= (expect), result, expect)
#define EXPECT_LT(result, expect) EXPECT_OP((result) < (expect), result, expect)
#define EXPECT_GE(result, expect) EXPECT_OP((result) >= (expect), result, expect)
#define EXPECT_GT(result, expect) EXPECT_OP((result) > (expect), result, expect)
#define EXPECT_TRUE(result) EXPECT_OP(result, result, true)
#define EXPECT_FALSE(result) EXPECT_OP(!result, result, false)
#define EXPECT_NEAR(result, expect, threshold) \
EXPECT_OP(is_equal(result, expect, threshold), result, expect)
struct ShaderTestOutput {
[[storage(0, write)]] TestOutput (&out_test)[];
};
#define TEST(a, b) if (true)

View File

@@ -0,0 +1,29 @@
/* SPDX-FileCopyrightText: 2026 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
bool tiled_image_lookup(float3 &co, sampler2DArray ima, sampler1DArray map)
{
float2 tile_pos = floor(co.xy);
if (tile_pos.x < 0 || tile_pos.y < 0 || tile_pos.x >= 10) {
return false;
}
float tile = 10 * tile_pos.y + tile_pos.x;
if (tile >= textureSize(map, 0).x) {
return false;
}
/* Fetch tile information. */
float tile_layer = texelFetch(map, int2(tile, 0), 0).x;
if (tile_layer < 0) {
return false;
}
float4 tile_info = texelFetch(map, int2(tile, 1), 0);
co = float3(((co.xy - tile_pos) * tile_info.zw) + tile_info.xy, tile_layer);
return true;
}

View File

@@ -0,0 +1,134 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
#ifndef FLT_MAX
# define FLT_MAX uintBitsToFloat(0x7F7FFFFFu)
# define FLT_MIN uintBitsToFloat(0x00800000u)
# define FLT_EPSILON 1.192092896e-07F
#endif
#ifndef SHRT_MAX
# define SHRT_MAX 0x00007FFF
# define INT_MAX 0x7FFFFFFF
# define USHRT_MAX 0x0000FFFFu
# define UINT_MAX 0xFFFFFFFFu
#endif
#define NAN_FLT uintBitsToFloat(0x7FC00000u)
#define FLT_11_MAX uintBitsToFloat(0x477E0000)
#define FLT_10_MAX uintBitsToFloat(0x477C0000)
#define FLT_11_11_10_MAX float3(FLT_11_MAX, FLT_11_MAX, FLT_10_MAX)
#define UNPACK2(a) (a)[0], (a)[1]
#define UNPACK3(a) (a)[0], (a)[1], (a)[2]
#define UNPACK4(a) (a)[0], (a)[1], (a)[2], (a)[3]
/**
* Clamp input into [0..1] range.
*/
#define saturate(a) clamp(a, 0.0f, 1.0f)
#define isfinite(a) (!isinf(a) && !isnan(a))
/* clang-format off */
#define in_range_inclusive(val, min_v, max_v) (all(greaterThanEqual(val, min_v)) && all(lessThanEqual(val, max_v)))
#define in_range_exclusive(val, min_v, max_v) (all(greaterThan(val, min_v)) && all(lessThan(val, max_v)))
#define in_texture_range(texel, tex) (all(greaterThanEqual(texel, int2(0))) && all(lessThan(texel, textureSize(tex, 0).xy)))
#define in_image_range(texel, tex) (all(greaterThanEqual(texel, int2(0))) && all(lessThan(texel, imageSize(tex).xy)))
#define weighted_sum(val0, val1, val2, val3, weights) ((val0 * weights[0] + val1 * weights[1] + val2 * weights[2] + val3 * weights[3]) * safe_rcp(weights[0] + weights[1] + weights[2] + weights[3]))
#define weighted_sum_array(val, weights) ((val[0] * weights[0] + val[1] * weights[1] + val[2] * weights[2] + val[3] * weights[3]) * safe_rcp(weights[0] + weights[1] + weights[2] + weights[3]))
/* clang-format on */
bool flag_test(uint flag, uint val)
{
return (flag & val) != 0u;
}
bool flag_test(int flag, uint val)
{
return flag_test(uint(flag), val);
}
bool flag_test(int flag, int val)
{
return (flag & val) != 0;
}
void set_flag_from_test(uint &value, bool test, uint flag)
{
if (test) {
value |= flag;
}
else {
value &= ~flag;
}
}
void set_flag_from_test(int &value, bool test, int flag)
{
if (test) {
value |= flag;
}
else {
value &= ~flag;
}
}
/* Keep define to match C++ implementation. */
#define SET_FLAG_FROM_TEST(value, test, flag) set_flag_from_test(value, test, flag)
/**
* Return true if the bit inside bitmask at bit_index is set high.
* Assume the lower bits are inside first component of bitmask,
*/
bool bitmask64_test(uint2 bitmask, uint bit_index)
{
uint bitmask32 = (bit_index >= 32u) ? bitmask.y : bitmask.x;
return flag_test(bitmask32, 1u << (bit_index & 0x1Fu));
}
/**
* Pack two 16-bit uint into one 32-bit uint.
*/
uint packUvec2x16(uint2 a)
{
a = (a & 0xFFFFu) << uint2(0u, 16u);
return a.x | a.y;
}
uint2 unpackUvec2x16(uint a)
{
return (uint2(a) >> uint2(0u, 16u)) & uint2(0xFFFFu);
}
/**
* Pack four 8-bit uint into one 32-bit uint.
*/
uint packUvec4x8(uint4 a)
{
a = (a & 0xFFu) << uint4(0u, 8u, 16u, 24u);
return a.x | a.y | a.z | a.w;
}
uint4 unpackUvec4x8(uint a)
{
return (uint4(a) >> uint4(0u, 8u, 16u, 24u)) & uint4(0xFFu);
}
/**
* Convert from float representation to ordered int allowing min/max atomic operation.
* Based on: https://stackoverflow.com/a/31010352
*/
int floatBitsToOrderedInt(float value)
{
/* Floats can be sorted using their bits interpreted as integers for positive values.
* Negative values do not follow int's two's complement ordering which is reversed.
* So we have to XOR all bits except the sign bits in order to reverse the ordering.
* Note that this is highly hardware dependent, but there seems to be no case of GPU where the
* ints ares not two's complement. */
int int_value = floatBitsToInt(value);
return (int_value < 0) ? (int_value ^ 0x7FFFFFFF) : int_value;
}
float orderedIntBitsToFloat(int int_value)
{
return intBitsToFloat((int_value < 0) ? (int_value ^ 0x7FFFFFFF) : int_value);
}

View File

@@ -0,0 +1,16 @@
/* SPDX-FileCopyrightText: 2018-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_2D_area_borders_infos.hh"
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_2D_area_borders)
void main()
{
/* Should be 1.0f but minimize the AA on the edges. */
float dist = (length(uv) - (0.98f - width)) * scale;
fragColor = color;
fragColor.a *= smoothstep(-0.09f, 1.09f, dist);
}

View File

@@ -0,0 +1,38 @@
/* SPDX-FileCopyrightText: 2018-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_2D_area_borders_infos.hh"
VERTEX_SHADER_CREATE_INFO(gpu_shader_2D_area_borders)
void main()
{
int corner_id = (gl_VertexID / cornerLen) % 4;
bool inner = all(lessThan(abs(pos), float2(1.0f)));
/* Scale the inner part of the border.
* Add a sub pixel offset to the outer part to make sure we don't miss a pixel row/column. */
float2 final_pos = pos * ((inner) ? (1.0f - width) : 1.05f);
uv = final_pos;
/* Rescale to the corner size and position the corner. */
if (corner_id == 0) {
/* top right */
final_pos = (final_pos - float2(1.0f, 1.0f)) * scale + rect.yw;
}
else if (corner_id == 1) {
/* top left */
final_pos = (final_pos - float2(-1.0f, 1.0f)) * scale + rect.xw;
}
else if (corner_id == 2) {
/* bottom left */
final_pos = (final_pos - float2(-1.0f, -1.0f)) * scale + rect.xz;
}
else {
/* bottom right */
final_pos = (final_pos - float2(1.0f, -1.0f)) * scale + rect.yz;
}
gl_Position = (ModelViewProjectionMatrix * float4(final_pos, 0.0f, 1.0f));
}

View File

@@ -0,0 +1,38 @@
/* SPDX-FileCopyrightText: 2018-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Simple shader that just draw one icon at the specified location
* does not need any vertex input (producing less call to immBegin/End)
*/
#include "infos/gpu_shader_2D_image_rect_color_infos.hh"
VERTEX_SHADER_CREATE_INFO(gpu_shader_2D_image_rect_color)
void main()
{
float2 uv;
float2 co;
if (gl_VertexID == 0) {
co = rect_geom.xw;
uv = rect_icon.xw;
}
else if (gl_VertexID == 1) {
co = rect_geom.xy;
uv = rect_icon.xy;
}
else if (gl_VertexID == 2) {
co = rect_geom.zw;
uv = rect_icon.zw;
}
else {
co = rect_geom.zy;
uv = rect_icon.zy;
}
gl_Position = ModelViewProjectionMatrix * float4(co, 0.0f, 1.0f);
texCoord_interp = uv;
}

View File

@@ -0,0 +1,13 @@
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_2D_image_infos.hh"
VERTEX_SHADER_CREATE_INFO(gpu_shader_2D_image_common)
void main()
{
gl_Position = ModelViewProjectionMatrix * float4(pos.xy, 0.0f, 1.0f);
texCoord_interp = texCoord;
}

View File

@@ -0,0 +1,36 @@
/* SPDX-FileCopyrightText: 2017-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Fragment Shader for dashed lines, with uniform multi-color(s),
* or any single-color, and any thickness.
*
* Dashed is performed in screen space.
*/
#include "infos/gpu_shader_line_dashed_uniform_color_infos.hh"
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_3D_line_dashed_uniform_color)
void main()
{
float distance_along_line = distance(stipple_pos, stipple_start);
/* Solid line case, simple. */
if (udash_factor >= 1.0f) {
fragColor = color;
}
/* Actually dashed line... */
else {
float normalized_distance = fract(distance_along_line / dash_width);
if (normalized_distance <= udash_factor) {
fragColor = color;
}
else if (colors_len > 0) {
fragColor = color2;
}
else {
gpu_discard_fragment();
}
}
}

View File

@@ -0,0 +1,139 @@
/* SPDX-FileCopyrightText: 2018-2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_2D_node_socket_infos.hh"
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_2D_node_socket_inst)
#include "gpu_shader_math_constants_lib.glsl"
#include "gpu_shader_math_matrix_construct_lib.glsl"
/* Values in `eNodeSocketDisplayShape` in DNA_node_types.h. Keep in sync. */
#define SOCK_DISPLAY_SHAPE_CIRCLE 0
#define SOCK_DISPLAY_SHAPE_SQUARE 1
#define SOCK_DISPLAY_SHAPE_DIAMOND 2
#define SOCK_DISPLAY_SHAPE_CIRCLE_DOT 3
#define SOCK_DISPLAY_SHAPE_SQUARE_DOT 4
#define SOCK_DISPLAY_SHAPE_DIAMOND_DOT 5
#define SOCK_DISPLAY_SHAPE_LINE 6
#define SOCK_DISPLAY_SHAPE_VOLUME_GRID 7
#define SOCK_DISPLAY_SHAPE_LIST 8
/* Calculates a squared distance field of a square. */
float square_sdf(float2 absCo, float2 half_size)
{
float2 extruded_co = absCo - half_size;
float2 clamped_extruded_co = float2(max(0.0f, extruded_co.x), max(0.0f, extruded_co.y));
float exterior_distance_squared = dot(clamped_extruded_co, clamped_extruded_co);
float interior_distance = min(max(extruded_co.x, extruded_co.y), 0.0f);
float interior_distance_squared = interior_distance * interior_distance;
return exterior_distance_squared - interior_distance_squared;
}
float2 rotate_45(float2 co)
{
return from_rotation(AngleRadian{M_PI * 0.25f}) * co;
}
/* Calculates an upper and lower limit for an anti-aliased cutoff of the squared distance. */
float2 calculate_thresholds(float threshold)
{
/* Use the absolute on one of the factors to preserve the sign. */
float inner_threshold = (threshold - 0.5f * AAsize) * abs(threshold - 0.5f * AAsize);
float outer_threshold = (threshold + 0.5f * AAsize) * abs(threshold + 0.5f * AAsize);
return float2(inner_threshold, outer_threshold);
}
void main()
{
float2 absUV = abs(uv);
float2 co = float2(max(absUV.x - extrusion.x, 0.0f), max(absUV.y - extrusion.y, 0.0f));
float distance_squared = 0.0f;
float alpha_threshold = 0.0f;
float dot_threshold = -1.0f;
constexpr float circle_radius = 0.5f;
const float square_radius = 0.5f / sqrt(2.0f / M_PI) * M_SQRT1_2;
const float diamond_radius = 0.5f / sqrt(2.0f / M_PI) * M_SQRT1_2;
constexpr float corner_rounding = 0.0f;
switch (finalShape) {
default:
case SOCK_DISPLAY_SHAPE_CIRCLE: {
distance_squared = dot(co, co);
alpha_threshold = circle_radius;
break;
}
case SOCK_DISPLAY_SHAPE_CIRCLE_DOT: {
distance_squared = dot(co, co);
alpha_threshold = circle_radius;
dot_threshold = finalDotRadius;
break;
}
case SOCK_DISPLAY_SHAPE_SQUARE: {
distance_squared = square_sdf(co, float2(square_radius - corner_rounding));
alpha_threshold = corner_rounding;
break;
}
case SOCK_DISPLAY_SHAPE_SQUARE_DOT: {
distance_squared = square_sdf(co, float2(square_radius - corner_rounding));
alpha_threshold = corner_rounding;
dot_threshold = finalDotRadius;
break;
}
case SOCK_DISPLAY_SHAPE_DIAMOND: {
distance_squared = square_sdf(abs(rotate_45(co)), float2(diamond_radius - corner_rounding));
alpha_threshold = corner_rounding;
break;
}
case SOCK_DISPLAY_SHAPE_DIAMOND_DOT: {
distance_squared = square_sdf(abs(rotate_45(co)), float2(diamond_radius - corner_rounding));
alpha_threshold = corner_rounding;
dot_threshold = finalDotRadius;
break;
}
case SOCK_DISPLAY_SHAPE_LINE: {
distance_squared = square_sdf(co, float2(square_radius * 0.75, square_radius * 1.4));
alpha_threshold = corner_rounding;
break;
}
case SOCK_DISPLAY_SHAPE_VOLUME_GRID: {
constexpr float rect_side_length = 0.25f;
const float2 oversize = float2(0.0f, square_radius * 1.4) / 2.5f;
const float2 rect_corner = max(float2(rect_side_length), extrusion / 2.0f + oversize) +
finalOutlineThickness / 4.0f;
const float2 mirrored_uv = abs(abs(uv) - rect_corner);
distance_squared = square_sdf(mirrored_uv, rect_corner + finalOutlineThickness / 2.0f);
alpha_threshold = corner_rounding;
break;
}
case SOCK_DISPLAY_SHAPE_LIST: {
constexpr float2 rect_side_length = float2(0.5f, 0.25f);
const float2 oversize = float2(0.0f, square_radius * 1.4) / 2.5f;
const float2 rect_corner = max(rect_side_length, extrusion / 2.0f + oversize) +
finalOutlineThickness / 4.0f;
const float2 mirrored_uv = float2(
abs(uv.x), abs(abs(abs(uv.y) - rect_corner.y / 1.5f) - rect_corner.y / 1.5f));
distance_squared = square_sdf(
mirrored_uv, (rect_corner + finalOutlineThickness / 2.0f) / float2(1.0f, 1.5f));
break;
}
}
float2 alpha_thresholds = calculate_thresholds(alpha_threshold);
float2 outline_thresholds = calculate_thresholds(alpha_threshold - finalOutlineThickness);
float2 dot_thresholds = calculate_thresholds(dot_threshold);
float alpha_mask = smoothstep(alpha_thresholds[1], alpha_thresholds[0], distance_squared);
float dot_mask = smoothstep(dot_thresholds[1], dot_thresholds[0], dot(co, co));
float outline_mask = smoothstep(outline_thresholds[0], outline_thresholds[1], distance_squared) +
dot_mask;
fragColor = mix(finalColor, finalOutlineColor, outline_mask);
fragColor.a *= alpha_mask;
}

View File

@@ -0,0 +1,70 @@
/* SPDX-FileCopyrightText: 2018-2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_2D_node_socket_infos.hh"
#include "gpu_shader_math_base_lib.glsl"
VERTEX_SHADER_CREATE_INFO(gpu_shader_2D_node_socket_inst)
#define rect parameters[widgetID * MAX_SOCKET_PARAMETERS + 0]
#define colorInner parameters[widgetID * MAX_SOCKET_PARAMETERS + 1]
#define colorOutline parameters[widgetID * MAX_SOCKET_PARAMETERS + 2]
#define outlineThickness parameters[widgetID * MAX_SOCKET_PARAMETERS + 3].x
#define outlineOffset parameters[widgetID * MAX_SOCKET_PARAMETERS + 3].y
#define shape parameters[widgetID * MAX_SOCKET_PARAMETERS + 3].z
#define aspect parameters[widgetID * MAX_SOCKET_PARAMETERS + 3].w
void main()
{
/* Scale the original rectangle to accommodate the diagonal of the diamond shape. */
float2 originalRectSize = rect.yw - rect.xz;
float offset = 0.125f * min(originalRectSize.x, originalRectSize.y) +
outlineOffset * outlineThickness;
float2 ofs = float2(offset, -offset);
float2 pos;
switch (gl_VertexID) {
default:
case 0: {
pos = rect.xz + ofs.yy;
break;
}
case 1: {
pos = rect.xw + ofs.yx;
break;
}
case 2: {
pos = rect.yz + ofs.xy;
break;
}
case 3: {
pos = rect.yw + ofs.xx;
break;
}
}
gl_Position = ModelViewProjectionMatrix * float4(pos, 0.0f, 1.0f);
float2 rectSize = rect.yw - rect.xz + 2.0f * float2(outlineOffset, outlineOffset);
float minSize = min(rectSize.x, rectSize.y);
float2 centeredCoordinates = pos - ((rect.xz + rect.yw) / 2.0f);
uv = centeredCoordinates / minSize;
/* Calculate the necessary "extrusion" of the coordinates to draw the middle part of
* multi sockets. */
float ratio = rectSize.x / rectSize.y;
extrusion = (ratio > 1.0f) ? float2((ratio - 1.0f) / 2.0f, 0.0f) :
float2(0.0f, ((1.0f / ratio) - 1.0f) / 2.0f);
/* Shape parameters. */
finalShape = int(shape);
finalOutlineThickness = outlineThickness / minSize;
finalDotRadius = outlineThickness / minSize;
AAsize = 1.0f * aspect / minSize;
/* Pass through parameters. */
finalColor = colorInner;
finalOutlineColor = colorOutline;
}

View File

@@ -0,0 +1,229 @@
/* SPDX-FileCopyrightText: 2018-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* 2D Cubic Bezier thick line drawing
*/
/**
* `uv.x` is position along the curve, defining the tangent space.
* `uv.y` is "signed" distance (compressed to [0..1] range) from the pos in expand direction
* `pos` is the verts position in the curve tangent space
*/
#pragma once
#include "gpu_shader_compat.hh"
#include "GPU_shader_shared.hh"
#include "gpu_shader_attribute_load_lib.glsl"
#include "gpu_shader_math_vector_compare_lib.glsl"
namespace builtin::nodelink {
struct NodeLinkVertIn {
[[attribute(0)]] float2 uv;
[[attribute(1)]] float2 pos;
[[attribute(2)]] float2 expand;
};
struct NodeLinkVertOut {
[[smooth]] float4 final_color;
[[smooth]] float2 line_uv;
[[flat]] float line_length;
[[flat]] float line_thickness;
[[flat]] float dash_length;
[[flat]] float dash_factor;
[[flat]] float dash_alpha;
[[flat]] float aspect;
[[flat]] int has_back_link;
[[flat]] int is_main_line;
};
struct NodeLinkFragOut {
[[frag_color(0)]] float4 color;
};
struct NodeLinkSRT {
[[push_constant]] float4x4 ModelViewProjectionMatrix;
[[storage(0, read)]] NodeLinkData (&link_data_buf)[];
[[uniform(0)]] NodeLinkUniformData &link_uniforms;
};
[[vertex]] void vert([[vertex_id]] const int gl_VertexID,
[[instance_id]] const int gl_InstanceID,
[[resource_table]] const NodeLinkSRT &srt,
[[in]] const NodeLinkVertIn &v_in,
[[position]] float4 &gl_Position,
[[out]] NodeLinkVertOut &interp)
{
constexpr float start_gradient_threshold = 0.35f;
constexpr float end_gradient_threshold = 0.65f;
const NodeLinkData link = srt.link_data_buf[gl_InstanceID];
const float2 P0 = link.bezier_P0;
const float2 P1 = link.bezier_P1;
const float2 P2 = link.bezier_P2;
const float2 P3 = link.bezier_P3;
const uint3 color_ids = gpu_attr_decode_uchar4_to_uint4(link.color_ids).xyz;
const float4 color_start = (color_ids[0] < 3u) ? link.start_color :
srt.link_uniforms.colors[color_ids[0]];
const float4 color_end = (color_ids[1] < 3u) ? link.end_color :
srt.link_uniforms.colors[color_ids[1]];
const float4 color_shadow = srt.link_uniforms.colors[color_ids[2]];
float line_thickness = link.thickness;
/* Each instance contains both the outline and the "main" line on top. */
constexpr int mid_vertex = 65;
bool is_outline_pass = gl_VertexID < mid_vertex;
interp.line_thickness = line_thickness;
interp.is_main_line = (v_in.expand.y == 1.0f && !is_outline_pass) ? 1 : 0;
interp.has_back_link = int(link.has_back_link);
interp.aspect = srt.link_uniforms.aspect;
/* Parameters for the dashed line. */
interp.dash_length = link.dash_length;
interp.dash_factor = link.dash_factor;
interp.dash_alpha = link.dash_alpha;
/* Approximate line length, no need for real bezier length calculation. */
interp.line_length = distance(P0, P3);
/* TODO: Incorrect U, this leads to non-uniform dash distribution. */
interp.line_uv = v_in.uv;
if ((v_in.expand.y == 1.0f) && link.has_back_link) {
/* Increase width because two links are drawn. */
line_thickness *= 1.7f;
}
if (is_outline_pass) {
/* Outline pass. */
interp.final_color = color_shadow;
}
else {
/* Second pass. */
if (v_in.uv.x < start_gradient_threshold) {
interp.final_color = color_start;
}
else if (v_in.uv.x > end_gradient_threshold) {
interp.final_color = color_end;
}
else {
float mixFactor = (v_in.uv.x - start_gradient_threshold) /
(end_gradient_threshold - start_gradient_threshold);
interp.final_color = mix(color_start, color_end, mixFactor);
}
line_thickness *= 0.65f;
if (link.do_muted) {
interp.final_color[3] = 0.65f;
}
}
interp.final_color.a *= link.dim_factor;
float t = v_in.uv.x;
float t2 = t * t;
float t2_3 = 3.0f * t2;
float one_minus_t = 1.0f - t;
float one_minus_t2 = one_minus_t * one_minus_t;
float one_minus_t2_3 = 3.0f * one_minus_t2;
float2 point = (P0 * one_minus_t2 * one_minus_t + P1 * one_minus_t2_3 * t +
P2 * t2_3 * one_minus_t + P3 * t2 * t);
float2 tangent = ((P1 - P0) * one_minus_t2_3 + (P2 - P1) * 6.0f * (t - t2) + (P3 - P2) * t2_3);
/* Tangent space at t. If the inner and outer control points overlap, the tangent is invalid.
* Use the vector between the sockets instead. */
tangent = is_zero(tangent) ? normalize(P3 - P0) : normalize(tangent);
float2 normal = tangent.yx * float2(-1.0f, 1.0f);
/* Position vertex on the curve tangent space */
point += (v_in.pos.x * tangent + v_in.pos.y * normal) * srt.link_uniforms.arrow_size;
gl_Position = srt.ModelViewProjectionMatrix * float4(point, 0.0f, 1.0f);
float2 exp_axis = v_in.expand.x * tangent + v_in.expand.y * normal;
/* rotate & scale the expand axis */
exp_axis = srt.ModelViewProjectionMatrix[0].xy * exp_axis.xx +
srt.ModelViewProjectionMatrix[1].xy * exp_axis.yy;
float expand_dist = line_thickness * (v_in.uv.y * 2.0f - 1.0f);
/* Expand into a line */
gl_Position.xy += exp_axis * srt.link_uniforms.aspect * expand_dist;
/* If the link is not muted or is not a reroute arrow the points are squashed to the center of
* the line. Magic numbers are defined in `drawnode.cc`. */
if ((v_in.expand.x == 1.0f && !link.do_muted) ||
(v_in.expand.y != 1.0f && (v_in.pos.x < 0.70f || v_in.pos.x > 0.71f) && !link.do_arrow))
{
gl_Position.xy *= 0.0f;
}
}
#define ANTIALIAS 0.75f
float get_line_alpha(float2 line_uv, float line_thickness, float center, float relative_radius)
{
float radius = relative_radius * line_thickness;
float sdf = abs(line_thickness * (line_uv.y - center));
return smoothstep(radius, radius - ANTIALIAS, sdf);
}
[[fragment]] void frag([[in]] const NodeLinkVertOut &interp, [[out]] NodeLinkFragOut &frag_out)
{
float dash_frag_alpha = 1.0f;
if (interp.dash_factor < 1.0f) {
float distance_along_line = interp.line_length * interp.line_uv.x;
/* Checking if `normalized_distance <= interp.dash_factor` is already enough for a basic
* dash, however we want to handle a nice anti-alias. */
float dash_center = interp.dash_length * interp.dash_factor * 0.5f;
float normalized_distance_triangle =
1.0f -
abs((fract((distance_along_line - dash_center) / interp.dash_length)) * 2.0f - 1.0f);
float t = interp.aspect * ANTIALIAS / interp.dash_length;
float slope = 1.0f / (2.0f * t);
float unclamped_alpha = 1.0f - slope * (normalized_distance_triangle - interp.dash_factor + t);
float alpha = max(interp.dash_alpha, min(unclamped_alpha, 1.0f));
dash_frag_alpha = alpha;
}
if (interp.is_main_line == 0) {
frag_out.color = interp.final_color;
frag_out.color.a *= get_line_alpha(interp.line_uv, interp.line_thickness, 0.5f, 0.5f) *
dash_frag_alpha;
return;
}
if (interp.has_back_link == 0) {
frag_out.color = interp.final_color;
frag_out.color.a *= get_line_alpha(interp.line_uv, interp.line_thickness, 0.5f, 0.5f) *
dash_frag_alpha;
}
else {
/* Draw two links right next to each other, the main link and the back-link. */
float4 main_link_color = interp.final_color;
main_link_color.a *= get_line_alpha(interp.line_uv, interp.line_thickness, 0.75f, 0.3f);
float4 back_link_color = float4(float3(0.8f), 1.0f);
back_link_color.a *= get_line_alpha(interp.line_uv, interp.line_thickness, 0.2f, 0.25f);
/* Combine both links. */
frag_out.color.rgb = main_link_color.rgb * main_link_color.a +
back_link_color.rgb * back_link_color.a;
frag_out.color.a = main_link_color.a * dash_frag_alpha + back_link_color.a;
}
}
} // namespace builtin::nodelink
PipelineGraphic gpu_shader_2D_nodelink(builtin::nodelink::vert, builtin::nodelink::frag);

View File

@@ -0,0 +1,23 @@
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_2D_point_uniform_size_uniform_color_aa_infos.hh"
VERTEX_SHADER_CREATE_INFO(gpu_shader_2D_point_uniform_size_uniform_color_aa)
void main()
{
gl_Position = ModelViewProjectionMatrix * float4(pos, 0.0f, 1.0f);
gl_PointSize = size;
/* calculate concentric radii in pixels */
float radius = 0.5f * size;
/* start at the outside and progress toward the center */
radii[0] = radius;
radii[1] = radius - 1.0f;
/* convert to PointCoord units */
radii /= size;
}

View File

@@ -0,0 +1,25 @@
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_2D_point_uniform_size_uniform_color_outline_aa_infos.hh"
VERTEX_SHADER_CREATE_INFO(gpu_shader_2D_point_uniform_size_uniform_color_outline_aa)
void main()
{
gl_Position = ModelViewProjectionMatrix * float4(pos, 0.0f, 1.0f);
gl_PointSize = size;
/* calculate concentric radii in pixels */
float radius = 0.5f * size;
/* start at the outside and progress toward the center */
radii[0] = radius;
radii[1] = radius - 1.0f;
radii[2] = radius - outlineWidth;
radii[3] = radius - outlineWidth - 1.0f;
/* convert to PointCoord units */
radii /= size;
}

View File

@@ -0,0 +1,14 @@
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_2D_point_varying_size_varying_color_infos.hh"
VERTEX_SHADER_CREATE_INFO(gpu_shader_2D_point_varying_size_varying_color)
void main()
{
gl_Position = ModelViewProjectionMatrix * float4(pos, 0.0f, 1.0f);
gl_PointSize = size;
finalColor = color;
}

View File

@@ -0,0 +1,761 @@
/* SPDX-FileCopyrightText: 2021 NVIDIA Corporation
* SPDX-FileCopyrightText: 2026 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0
*
* Adapted code from NVIDIA Corporation. */
#pragma once
#include "gpu_shader_utildefines_lib.glsl"
namespace builtin::mipmaps {
/* Conversion functions. */
template<typename DstType, typename SrcType>
void convert(DstType & /*dst_value*/, const SrcType /*src_value*/)
{
}
template<> void convert<float4, float>(float4 &dst_value, const float src_value)
{
dst_value.x = src_value;
dst_value.y = 0.0;
dst_value.z = 0.0;
dst_value.w = 0.0;
}
template<> void convert<float, float4>(float &dst_value, const float4 src_value)
{
dst_value = src_value.x;
}
template<> void convert<float4, float4>(float4 &dst_value, const float4 src_value)
{
dst_value = src_value;
}
/* Color transfer functions */
/* TODO: should be moved to a library */
float srgb_to_linearrgb(float c)
{
if (c < 0.04045f) {
return (c < 0.0f) ? 0.0f : c * (1.0f / 12.92f);
}
return pow((c + 0.055f) * (1.0f / 1.055f), 2.4f);
}
float linearrgb_to_srgb(float c)
{
if (c < 0.0031308f) {
return (c < 0.0f) ? 0.0f : c * 12.92f;
}
return 1.055f * pow(c, 1.0f / 2.4f) - 0.055f;
}
/**
* General-case shader for generating 1 or 2 levels of the mip pyramid.
* When generating 1 level, each workgroup handles up to 128 samples of the
* output mip level. When generating 2 levels, each workgroup handles
* a 8x8 tile of the last (2nd) output mip level, generating up to
* 17x17 samples of the intermediate (1st) output mip level along the way.
*
* Dispatch with y, z = 1
*/
#define LOCAL_SIZE_X 128
#define TILE_SIZE 8
#define MAX_SHARED_SAMPLES (TILE_SIZE + TILE_SIZE + 1)
#define INPUT_LEVEL 0
/** Shared storage that can store intermediate results using without encoding. */
template<typename T> struct Shared {
/**
* When generating 2 levels, the results the first level are cached here; this is the input tile
* needed to generate the 8x8 tile of the second level.
*/
[[shared]] T intermediate_level[MAX_SHARED_SAMPLES][MAX_SHARED_SAMPLES];
void store_sample(int2 dst_coord, T color)
{
intermediate_level[dst_coord.y][dst_coord.x] = color;
}
T load_sample(int2 src_coord)
{
return intermediate_level[src_coord.y][src_coord.x];
}
};
/** Shared storage that can store intermediate results encoded as uint. */
struct SharedUnorm {
/**
* When generating 2 levels, the results the first level are cached here; this is the input tile
* needed to generate the 8x8 tile of the second level.
*/
[[shared]] uint intermediate_level[MAX_SHARED_SAMPLES][MAX_SHARED_SAMPLES];
void store_sample(int2 dst_coord, float color)
{
uint encoded = uint(clamp(color, 0.0f, 1.0f) * UINT_MAX);
intermediate_level[dst_coord.y][dst_coord.x] = encoded;
}
float load_sample(int2 src_coord)
{
uint encoded = intermediate_level[src_coord.y][src_coord.x];
return float(encoded) / UINT_MAX;
}
};
/** Shared storage that can store intermediate results in an SRGB encoded uint. */
struct SharedSRGB {
/**
* When generating 2 levels, the results the first level are cached here; this is the input tile
* needed to generate the 8x8 tile of the second level.
*/
[[shared]] uint intermediate_level[MAX_SHARED_SAMPLES][MAX_SHARED_SAMPLES];
void store_sample(int2 dst_coord, float4 color)
{
float4 srgba;
srgba.r = linearrgb_to_srgb(color.r);
srgba.g = linearrgb_to_srgb(color.g);
srgba.b = linearrgb_to_srgb(color.b);
srgba.a = color.a;
uint srgb_packed = packUnorm4x8(srgba);
intermediate_level[dst_coord.y][dst_coord.x] = srgb_packed;
}
float4 load_sample(int2 src_coord)
{
uint srgb_packed = intermediate_level[src_coord.y][src_coord.x];
float4 srgba = unpackUnorm4x8(srgb_packed);
float4 linear_color;
linear_color.r = srgb_to_linearrgb(srgba.r);
linear_color.g = srgb_to_linearrgb(srgba.g);
linear_color.b = srgb_to_linearrgb(srgba.b);
linear_color.a = srgba.a;
return linear_color;
}
};
int2 kernel_size_from_input_size(int2 input_size)
{
return int2(input_size.x == 1 ? 1 : (2 | (input_size.x & 1)),
input_size.y == 1 ? 1 : (2 | (input_size.y & 1)));
}
/**
* \brief Templated struct for bindings and performing the mipmap generation.
*
* The mipmap generation is based on the general algorithm of
* https://github.com/nvpro-samples/vk_compute_mipmaps/tree/main/nvpro_pyramid
* It can generate 2 mipmap levels per dispatch.
*
* \param format: is the texture format of the mipmap images.
*
* \param SharedStorage: is the storage class to store intermediate levels. Depending on the
* texture format an optimal storage class can be selected.
*
* \param InnerType: the type to use for computation. Depending on the number of samples that a
* texture format has a more memory efficient type can be used.
*/
template<enum TextureWriteFormat format, typename SharedStorage, typename InnerType>
struct Resources {
[[compilation_constant]] const bool is_srgb_texture;
[[compilation_constant]] const bool is_layered;
[[push_constant]] const int num_levels;
[[image(0, read, format), condition(!is_layered)]] image2D mip_in;
[[image(1, write, format), condition(!is_layered)]] image2D mip_out1;
[[image(2, write, format), condition(!is_layered)]] image2D mip_out2;
[[image(0, read, format), condition(is_layered)]] image2DArray mip_array_in;
[[image(1, write, format), condition(is_layered)]] image2DArray mip_array_out1;
[[image(2, write, format), condition(is_layered)]] image2DArray mip_array_out2;
[[resource_table]] srt_t<SharedStorage> shared_storage;
/** Store sample result into an output mip image. */
void store_sample(int2 dst_coord, int dst_level, InnerType color)
{
float4 color_out;
convert<float4, InnerType>(color_out, color);
if (is_srgb_texture) [[static_branch]] {
color_out.r = linearrgb_to_srgb(color_out.r);
color_out.g = linearrgb_to_srgb(color_out.g);
color_out.b = linearrgb_to_srgb(color_out.b);
}
if (is_layered == false) [[static_branch]] {
if (dst_level == 1) {
imageStore(mip_out1, dst_coord, color_out);
}
else if (dst_level == 2) {
imageStore(mip_out2, dst_coord, color_out);
}
}
if (is_layered) [[static_branch]] {
if (dst_level == 1) {
imageStore(mip_array_out1, int3(dst_coord, 0), color_out);
}
else if (dst_level == 2) {
imageStore(mip_array_out2, int3(dst_coord, 0), color_out);
}
}
}
void store_shared_sample(int2 dst_coord, InnerType color)
{
SharedStorage &storage = shared_storage;
storage.store_sample(dst_coord, color);
}
InnerType load_sample(int2 src_coord, bool load_from_shared)
{
InnerType color;
if (load_from_shared) {
SharedStorage &storage = shared_storage;
color = storage.load_sample(src_coord);
}
else {
float4 loaded_color;
if (is_layered == false) [[static_branch]] {
loaded_color = imageLoad(mip_in, src_coord);
}
if (is_layered) [[static_branch]] {
loaded_color = imageLoad(mip_array_in, int3(src_coord, 0));
}
if (is_srgb_texture) [[static_branch]] {
loaded_color.r = srgb_to_linearrgb(loaded_color.r);
loaded_color.g = srgb_to_linearrgb(loaded_color.g);
loaded_color.b = srgb_to_linearrgb(loaded_color.b);
}
convert<InnerType, float4>(color, loaded_color);
}
return color;
}
int2 level_size(int level)
{
int2 mip_in_size;
if (is_layered == false) [[static_branch]] {
mip_in_size = imageSize(mip_in);
}
if (is_layered) [[static_branch]] {
mip_in_size = imageSize(mip_array_in).xy;
}
int2 mip_size = max((mip_in_size >> level), int2(1));
return mip_size;
}
InnerType pyramid_reduce_3(
float a0, InnerType v0, float a1, InnerType v1, float a2, InnerType v2)
{
return a0 * v0 + a1 * v1 + a2 * v2;
}
InnerType pyramid_reduce_2(InnerType v0, InnerType v1)
{
return 0.5 * (v0 + v1);
}
/**
* Handle loading and reducing a rectangle of size kernel_size
* with the given upper-left coordinate src_coord. Samples read from
* mip level src_level if !loadFromShared_, sharedLevel_ otherwise.
*
* kernel_size must range from 1x1 to 3x3.
*
* Once computed, the sample is written to the given coordinate of the
* specified destination mip level, and returned. The destination
* image size is needed to compute the kernel weights.
*/
template<bool load_from_shared>
InnerType reduce_store_sample(int2 src_coord,
int /*src_level*/,
int2 kernel_size,
int2 dst_image_size,
int2 dst_coord,
int dst_level)
{
float num_dst_pixels = dst_image_size.y;
float rcp = 1.0f / (2 * num_dst_pixels + 1);
float w0 = rcp * (num_dst_pixels - dst_coord.y);
float w1 = rcp * num_dst_pixels;
float w2 = 1.0f - w0 - w1;
InnerType v0, v1, v2, h0, h1, h2, out_pixel;
/* Reduce vertically up to 3 times (depending on kernel horizontal size) */
switch (kernel_size.x) {
case 3:
switch (kernel_size.y) {
case 3:
v2 = load_sample(src_coord + int2(2, 2), load_from_shared);
ATTR_FALLTHROUGH;
case 2:
v1 = load_sample(src_coord + int2(2, 1), load_from_shared);
ATTR_FALLTHROUGH;
case 1:
v0 = load_sample(src_coord + int2(2, 0), load_from_shared);
break;
}
switch (kernel_size.y) {
case 3:
h2 = pyramid_reduce_3(w0, v0, w1, v1, w2, v2);
break;
case 2:
h2 = pyramid_reduce_2(v0, v1);
break;
case 1:
h2 = v0;
break;
}
ATTR_FALLTHROUGH;
case 2:
switch (kernel_size.y) {
case 3:
v2 = load_sample(src_coord + int2(1, 2), load_from_shared);
ATTR_FALLTHROUGH;
case 2:
v1 = load_sample(src_coord + int2(1, 1), load_from_shared);
ATTR_FALLTHROUGH;
case 1:
v0 = load_sample(src_coord + int2(1, 0), load_from_shared);
break;
}
switch (kernel_size.y) {
case 3:
h1 = pyramid_reduce_3(w0, v0, w1, v1, w2, v2);
break;
case 2:
h1 = pyramid_reduce_2(v0, v1);
break;
case 1:
h1 = v0;
break;
}
ATTR_FALLTHROUGH;
case 1:
switch (kernel_size.y) {
case 3:
v2 = load_sample(src_coord + int2(0, 2), load_from_shared);
ATTR_FALLTHROUGH;
case 2:
v1 = load_sample(src_coord + int2(0, 1), load_from_shared);
ATTR_FALLTHROUGH;
case 1:
v0 = load_sample(src_coord + int2(0, 0), load_from_shared);
break;
}
switch (kernel_size.y) {
case 3:
h0 = pyramid_reduce_3(w0, v0, w1, v1, w2, v2);
break;
case 2:
h0 = pyramid_reduce_2(v0, v1);
break;
case 1:
h0 = v0;
break;
}
}
/* Reduce up to 3 samples horizontally. */
switch (kernel_size.x) {
case 3:
num_dst_pixels = dst_image_size.x;
rcp = 1.0f / (2 * num_dst_pixels + 1);
w0 = rcp * (num_dst_pixels - dst_coord.x);
w1 = rcp * num_dst_pixels;
w2 = 1.0f - w0 - w1;
out_pixel = pyramid_reduce_3(w0, h0, w1, h1, w2, h2);
break;
case 2:
out_pixel = pyramid_reduce_2(h0, h1);
break;
case 1:
out_pixel = h0;
}
/* Write out sample. */
store_sample(dst_coord, dst_level, out_pixel);
return out_pixel;
}
/**
* Compute and write out (to the 1st mip level generated) the samples
* at coordinates
* init_dst_coord,
* init_dst_coord + step, ...
* init_dst_coord + (iterations-1) * step
* and cache them at in the sharedLevel_ tile at coordinates
* init_shared_coord,
* init_shared_coord + step, ...
* init_shared_coord + (iterations-1) * step
* If use_bounds_check is true, skip coordinates that are out of bounds.
*/
void intermediate_level_loop(int2 init_dst_coord,
int2 init_shared_coord,
int2 step,
int iterations,
bool use_bounds_check)
{
int2 dst_coord = init_dst_coord;
int2 shared_coord = init_shared_coord;
int src_level = INPUT_LEVEL;
int dst_level = src_level + 1;
int2 src_image_size = level_size(src_level);
int2 dst_image_size = level_size(dst_level);
int2 kernel_size = kernel_size_from_input_size(src_image_size);
for (int i_ = 0; i_ < iterations; ++i_) {
int2 src_coord = dst_coord * 2;
if (use_bounds_check) {
if (uint(dst_coord.x) >= uint(dst_image_size.x)) {
continue;
}
if (uint(dst_coord.y) >= uint(dst_image_size.y)) {
continue;
}
}
InnerType result = reduce_store_sample<false>(
src_coord, src_level, kernel_size, dst_image_size, dst_coord, dst_level);
/* `reduce_store_sample` handles writing to the actual output; manually
* cache into shared memory here. */
store_shared_sample(shared_coord, result);
dst_coord += step;
shared_coord += step;
}
}
/**
* Function for the workgroup that handles filling the intermediate level
* (caching it in shared memory as well).
*
* We need somewhere from 16x16 to 17x17 samples, depending
* on what the kernel size for the 2nd mip level generation will be.
*
* dst_tile_coord : upper left coordinate of the tile to generate.
* use_bounds_check : whether to skip samples that are out-of-bounds.
*/
void fill_intermediate_tile(uint local_index, int2 dst_tile_coord, bool use_bounds_check)
{
int2 init_thread_offset;
int2 step;
int iterations;
int2 dst_image_size = level_size(INPUT_LEVEL + 1);
int2 future_kernel_size = kernel_size_from_input_size(dst_image_size);
if (future_kernel_size.x == 3) {
if (future_kernel_size.y == 3) {
/* Fill in 2 17x7 steps and 1 17x3 step (9 idle threads) */
init_thread_offset = int2(local_index % 17u, local_index / 17u);
step = int2(0, 7);
iterations = local_index >= 7 * 17 ? 0 : local_index < 3 * 17 ? 3 : 2;
}
else {
/* Future 3x[2,1] kernel
* Fill in 2 8x16 steps and 1 1x16 step */
init_thread_offset = int2(local_index / 16u, local_index % 16u);
step = int2(8, 0);
iterations = local_index < 1 * 16 ? 3 : 2;
}
}
else {
if (future_kernel_size.y == 3) {
/* Fill in 2 16x8 steps and 1 16x1 step */
init_thread_offset = int2(local_index % 16u, local_index / 16u);
step = int2(0, 8);
iterations = local_index < 1 * 16 ? 3 : 2;
}
else {
/* Fill in 2 16x8 steps */
init_thread_offset = int2(local_index % 16u, local_index / 16u);
step = int2(0, 8);
iterations = 2;
}
}
intermediate_level_loop(dst_tile_coord + init_thread_offset,
init_thread_offset,
step,
iterations,
use_bounds_check);
}
/**
* Function for the workgroup that handles filling the last level tile
* (2nd level after the original input level), using as input the
* tile in shared memory.
*
* dst_tile_coord : upper left coordinate of the tile to generate.
* use_bounds_check : whether to skip samples that are out-of-bounds.
*/
void fill_last_tile(uint local_index, int2 dst_tile_coord, bool use_bounds_check)
{
if (local_index < 8 * 8) {
int2 thread_offset = int2(local_index % 8u, local_index / 8u);
int src_level = INPUT_LEVEL + 1;
int dst_level = INPUT_LEVEL + 2;
int2 src_image_size = level_size(src_level);
int2 dst_image_size = level_size(dst_level);
int2 src_shared_coord = thread_offset * 2;
int2 kernel_size = kernel_size_from_input_size(src_image_size);
int2 dst_coord = thread_offset + dst_tile_coord;
bool within_bounds = true;
if (use_bounds_check) {
within_bounds = (uint(dst_coord.x) < uint(dst_image_size.x)) &&
(uint(dst_coord.y) < uint(dst_image_size.y));
}
if (within_bounds) {
reduce_store_sample<true>(
src_shared_coord, 0, kernel_size, dst_image_size, dst_coord, dst_level);
}
}
}
};
template<enum TextureWriteFormat format, typename SharedStorage, typename InnerType>
[[local_size(LOCAL_SIZE_X)]] [[compute]]
void update_mipmaps([[global_invocation_id]] const uint3 global_id,
[[work_group_id]] const uint3 group_id,
[[local_invocation_id]] const uint3 local_index,
[[resource_table]] Resources<format, SharedStorage, InnerType> &srt)
{
if (srt.num_levels == 1u) {
int2 kernel_size = kernel_size_from_input_size(srt.level_size(INPUT_LEVEL));
int2 dst_image_size = srt.level_size(INPUT_LEVEL + 1);
int2 dst_coord = int2(int(global_id.x) % dst_image_size.x,
int(global_id.x) / dst_image_size.x);
int2 src_coord = dst_coord * 2;
if (dst_coord.y < dst_image_size.y) {
srt.template reduce_store_sample<false>(
src_coord, INPUT_LEVEL, kernel_size, dst_image_size, dst_coord, INPUT_LEVEL + 1);
}
}
else {
/* Handling two levels.
* Assign a 8x8 tile of mip level inputLevel_ + 2 to this workgroup. */
int level2 = INPUT_LEVEL + 2;
int2 level2_size = srt.level_size(level2);
int2 tile_count;
tile_count.x = int(uint(level2_size.x + 7) / 8u);
tile_count.y = int(uint(level2_size.y + 7) / 8u);
int2 tile_index = int2(group_id.x % uint(tile_count.x), group_id.x / uint(tile_count.x));
/* Determine if bounds checking is needed; this is only the case
* for tiles at the right or bottom fringe that might be cut off
* by the image border. Note that later, I use if statements rather
* than passing use_bounds_check directly to convince the compiler
* to inline everything. */
bool use_bounds_check = tile_index.x >= tile_count.x - 1 || tile_index.y >= tile_count.y - 1;
if (use_bounds_check) {
/* Compute the tile in level inputLevel_ + 1 that's needed to
* compute the above 8x8 tile. */
srt.fill_intermediate_tile(local_index.x, tile_index * 2 * int2(8, 8), true);
barrier();
/* Compute the inputLevel_ + 2 tile of size 8x8, loading
* inputs from shared memory. */
srt.fill_last_tile(local_index.x, tile_index * int2(8, 8), true);
}
else {
/* Same but without bounds checking. */
srt.fill_intermediate_tile(local_index.x, tile_index * 2 * int2(8, 8), false);
barrier();
srt.fill_last_tile(local_index.x, tile_index * int2(8, 8), false);
}
}
}
template struct Shared<float>;
template struct Shared<float4>;
template struct Resources<UNORM_8, SharedUnorm, float>;
template struct Resources<UNORM_8_8_8_8, SharedSRGB, float4>;
template struct Resources<SFLOAT_16, Shared<float>, float>;
template struct Resources<SFLOAT_16_16_16_16, Shared<float4>, float4>;
template struct Resources<SFLOAT_32, Shared<float>, float>;
template struct Resources<SFLOAT_32_32_32_32, Shared<float4>, float4>;
template float Resources<UNORM_8, SharedUnorm, float>::reduce_store_sample<true>(
int2 src_coord,
int src_level,
int2 kernel_size,
int2 dst_image_size,
int2 dst_coord,
int dst_level);
template float Resources<UNORM_8, SharedUnorm, float>::reduce_store_sample<false>(
int2 src_coord,
int src_level,
int2 kernel_size,
int2 dst_image_size,
int2 dst_coord,
int dst_level);
template float4 Resources<UNORM_8_8_8_8, SharedSRGB, float4>::reduce_store_sample<true>(
int2 src_coord,
int src_level,
int2 kernel_size,
int2 dst_image_size,
int2 dst_coord,
int dst_level);
template float4 Resources<UNORM_8_8_8_8, SharedSRGB, float4>::reduce_store_sample<false>(
int2 src_coord,
int src_level,
int2 kernel_size,
int2 dst_image_size,
int2 dst_coord,
int dst_level);
template float Resources<SFLOAT_16, Shared<float>, float>::reduce_store_sample<true>(
int2 src_coord,
int src_level,
int2 kernel_size,
int2 dst_image_size,
int2 dst_coord,
int dst_level);
template float Resources<SFLOAT_16, Shared<float>, float>::reduce_store_sample<false>(
int2 src_coord,
int src_level,
int2 kernel_size,
int2 dst_image_size,
int2 dst_coord,
int dst_level);
template float4 Resources<SFLOAT_16_16_16_16, Shared<float4>, float4>::reduce_store_sample<true>(
int2 src_coord,
int src_level,
int2 kernel_size,
int2 dst_image_size,
int2 dst_coord,
int dst_level);
template float4 Resources<SFLOAT_16_16_16_16, Shared<float4>, float4>::reduce_store_sample<false>(
int2 src_coord,
int src_level,
int2 kernel_size,
int2 dst_image_size,
int2 dst_coord,
int dst_level);
template float Resources<SFLOAT_32, Shared<float>, float>::reduce_store_sample<true>(
int2 src_coord,
int src_level,
int2 kernel_size,
int2 dst_image_size,
int2 dst_coord,
int dst_level);
template float Resources<SFLOAT_32, Shared<float>, float>::reduce_store_sample<false>(
int2 src_coord,
int src_level,
int2 kernel_size,
int2 dst_image_size,
int2 dst_coord,
int dst_level);
template float4 Resources<SFLOAT_32_32_32_32, Shared<float4>, float4>::reduce_store_sample<true>(
int2 src_coord,
int src_level,
int2 kernel_size,
int2 dst_image_size,
int2 dst_coord,
int dst_level);
template float4 Resources<SFLOAT_32_32_32_32, Shared<float4>, float4>::reduce_store_sample<false>(
int2 src_coord,
int src_level,
int2 kernel_size,
int2 dst_image_size,
int2 dst_coord,
int dst_level);
template void update_mipmaps<UNORM_8, SharedUnorm, float>(
const uint3 global_id,
const uint3 group_id,
const uint3 local_index,
Resources<UNORM_8, SharedUnorm, float> &srt);
template void update_mipmaps<UNORM_8_8_8_8, SharedSRGB, float4>(
const uint3 global_id,
const uint3 group_id,
const uint3 local_index,
Resources<UNORM_8_8_8_8, SharedSRGB, float4> &srt);
template void update_mipmaps<SFLOAT_16, Shared<float>, float>(
const uint3 global_id,
const uint3 group_id,
const uint3 local_index,
Resources<SFLOAT_16, Shared<float>, float> &srt);
template void update_mipmaps<SFLOAT_16_16_16_16, Shared<float4>, float4>(
const uint3 global_id,
const uint3 group_id,
const uint3 local_index,
Resources<SFLOAT_16_16_16_16, Shared<float4>, float4> &srt);
template void update_mipmaps<SFLOAT_32, Shared<float>, float>(
const uint3 global_id,
const uint3 group_id,
const uint3 local_index,
Resources<SFLOAT_32, Shared<float>, float> &srt);
template void update_mipmaps<SFLOAT_32_32_32_32, Shared<float4>, float4>(
const uint3 global_id,
const uint3 group_id,
const uint3 local_index,
Resources<SFLOAT_32_32_32_32, Shared<float4>, float4> &srt);
} // namespace builtin::mipmaps
PipelineCompute gpu_shader_2D_update_mipmaps_unorm_8(
builtin::mipmaps::update_mipmaps<UNORM_8, builtin::mipmaps::SharedUnorm, float>,
builtin::mipmaps::Resources<UNORM_8, builtin::mipmaps::SharedUnorm, float>{
.is_srgb_texture = false, .is_layered = false});
PipelineCompute gpu_shader_2D_update_mipmaps_unorm_8_layered(
builtin::mipmaps::update_mipmaps<UNORM_8, builtin::mipmaps::SharedUnorm, float>,
builtin::mipmaps::Resources<UNORM_8, builtin::mipmaps::SharedUnorm, float>{
.is_srgb_texture = false, .is_layered = true});
PipelineCompute gpu_shader_2D_update_mipmaps_unorm_8_8_8_8(
builtin::mipmaps::update_mipmaps<UNORM_8_8_8_8, builtin::mipmaps::SharedSRGB, float4>,
builtin::mipmaps::Resources<UNORM_8_8_8_8, builtin::mipmaps::SharedSRGB, float4>{
.is_srgb_texture = false, .is_layered = false});
PipelineCompute gpu_shader_2D_update_mipmaps_unorm_8_8_8_8_layered(
builtin::mipmaps::update_mipmaps<UNORM_8_8_8_8, builtin::mipmaps::SharedSRGB, float4>,
builtin::mipmaps::Resources<UNORM_8_8_8_8, builtin::mipmaps::SharedSRGB, float4>{
.is_srgb_texture = false, .is_layered = true});
PipelineCompute gpu_shader_2D_update_mipmaps_sfloat_16(
builtin::mipmaps::update_mipmaps<SFLOAT_16, builtin::mipmaps::Shared<float>, float>,
builtin::mipmaps::Resources<SFLOAT_16, builtin::mipmaps::Shared<float>, float>{
.is_srgb_texture = false, .is_layered = false});
PipelineCompute gpu_shader_2D_update_mipmaps_sfloat_16_layered(
builtin::mipmaps::update_mipmaps<SFLOAT_16, builtin::mipmaps::Shared<float>, float>,
builtin::mipmaps::Resources<SFLOAT_16, builtin::mipmaps::Shared<float>, float>{
.is_srgb_texture = false, .is_layered = true});
PipelineCompute gpu_shader_2D_update_mipmaps_sfloat_16_16_16_16(
builtin::mipmaps::update_mipmaps<SFLOAT_16_16_16_16, builtin::mipmaps::Shared<float4>, float4>,
builtin::mipmaps::Resources<SFLOAT_16_16_16_16, builtin::mipmaps::Shared<float4>, float4>{
.is_srgb_texture = false, .is_layered = false});
PipelineCompute gpu_shader_2D_update_mipmaps_sfloat_16_16_16_16_layered(
builtin::mipmaps::update_mipmaps<SFLOAT_16_16_16_16, builtin::mipmaps::Shared<float4>, float4>,
builtin::mipmaps::Resources<SFLOAT_16_16_16_16, builtin::mipmaps::Shared<float4>, float4>{
.is_srgb_texture = false, .is_layered = true});
PipelineCompute gpu_shader_2D_update_mipmaps_sfloat_32(
builtin::mipmaps::update_mipmaps<SFLOAT_32, builtin::mipmaps::Shared<float>, float>,
builtin::mipmaps::Resources<SFLOAT_32, builtin::mipmaps::Shared<float>, float>{
.is_srgb_texture = false, .is_layered = false});
PipelineCompute gpu_shader_2D_update_mipmaps_sfloat_32_layered(
builtin::mipmaps::update_mipmaps<SFLOAT_32, builtin::mipmaps::Shared<float>, float>,
builtin::mipmaps::Resources<SFLOAT_32, builtin::mipmaps::Shared<float>, float>{
.is_srgb_texture = false, .is_layered = true});
PipelineCompute gpu_shader_2D_update_mipmaps_sfloat_32_32_32_32(
builtin::mipmaps::update_mipmaps<SFLOAT_32_32_32_32, builtin::mipmaps::Shared<float4>, float4>,
builtin::mipmaps::Resources<SFLOAT_32_32_32_32, builtin::mipmaps::Shared<float4>, float4>{
.is_srgb_texture = false, .is_layered = false});
PipelineCompute gpu_shader_2D_update_mipmaps_sfloat_32_32_32_32_layered(
builtin::mipmaps::update_mipmaps<SFLOAT_32_32_32_32, builtin::mipmaps::Shared<float4>, float4>,
builtin::mipmaps::Resources<SFLOAT_32_32_32_32, builtin::mipmaps::Shared<float4>, float4>{
.is_srgb_texture = false, .is_layered = true});
PipelineCompute gpu_shader_2D_update_mipmaps_srgba_8_8_8_8(
builtin::mipmaps::update_mipmaps<UNORM_8_8_8_8, builtin::mipmaps::SharedSRGB, float4>,
builtin::mipmaps::Resources<UNORM_8_8_8_8, builtin::mipmaps::SharedSRGB, float4>{
.is_srgb_texture = true, .is_layered = false});
PipelineCompute gpu_shader_2D_update_mipmaps_srgba_8_8_8_8_layered(
builtin::mipmaps::update_mipmaps<UNORM_8_8_8_8, builtin::mipmaps::SharedSRGB, float4>,
builtin::mipmaps::Resources<UNORM_8_8_8_8, builtin::mipmaps::SharedSRGB, float4>{
.is_srgb_texture = true, .is_layered = true});

View File

@@ -0,0 +1,12 @@
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_2D_checker_infos.hh"
VERTEX_SHADER_CREATE_INFO(gpu_shader_2D_checker)
void main()
{
gl_Position = ModelViewProjectionMatrix * float4(pos, 0.0f, 1.0f);
}

View File

@@ -0,0 +1,410 @@
/* SPDX-FileCopyrightText: 2018-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
#include "GPU_shader_shared.hh"
#include "gpu_interface_infos.hh"
#include "gpu_shader_colorspace_lib.glsl"
#include "gpu_shader_create_info.hh"
/* TODO(fclem): Share with C code. */
#define MAX_PARAM 12
#define MAX_INSTANCE 6
namespace builtin::widget {
struct VertOut {
[[flat]] float discard_fac;
[[flat]] float line_width;
[[flat]] float2 out_rect_size;
[[flat]] float4 border_color;
[[flat]] float4 emboss_color;
[[flat]] float4 out_round_corners;
[[no_perspective]] float but_co;
[[no_perspective]] float2 uv_interp;
[[no_perspective]] float4 inner_color;
};
struct [[host_shared]] WidgetRaw {
float4 data[12];
};
struct [[host_shared]] Widget {
float4 recti;
float4 rect;
float radsi;
float rads;
float2 faci;
float4 round_corners;
float4 color_inner1;
float4 color_inner2;
float4 color_edge;
float4 color_emboss;
float4 color_tria;
float2 tria1_center;
float2 tria2_center;
float tria1_size;
float tria2_size;
float shade_dir;
float alpha_discard;
float tria_type;
float _pad0;
float _pad1;
float _pad2;
/* We encode alpha check and discard factor together. */
bool do_alpha_check() const
{
return alpha_discard < 0.0f;
}
float discard_factor() const
{
return abs(alpha_discard);
}
VertOut do_widget(int vert_id, float2 &pos)
{
VertOut v_out;
/* Offset to avoid losing pixels (mimics conservative rasterization). */
constexpr float2 ofs = float2(0.5f, -0.5f);
v_out.line_width = abs(rect.x - recti.x);
float2 emboss_ofs = float2(0.0f, -v_out.line_width);
switch (vert_id) {
default:
case 0: {
pos = rect.xz + emboss_ofs + ofs.yy;
break;
}
case 1: {
pos = rect.xw + ofs.yx;
break;
}
case 2: {
pos = rect.yz + emboss_ofs + ofs.xy;
break;
}
case 3: {
pos = rect.yw + ofs.xx;
break;
}
}
v_out.uv_interp = pos - rect.xz;
v_out.out_rect_size = rect.yw - rect.xz;
v_out.out_round_corners = rads * round_corners;
float2 uv = v_out.uv_interp / v_out.out_rect_size;
float fac = clamp((shade_dir > 0.0f) ? uv.y : uv.x, 0.0f, 1.0f);
/* Note innerColor is premultiplied inside the fragment shader. */
if (do_alpha_check()) {
v_out.inner_color = color_inner1;
v_out.but_co = uv.x;
}
else {
v_out.inner_color = mix(color_inner2, color_inner1, fac);
v_out.but_co = -abs(uv.x);
}
/* We need premultiplied color for transparency. */
v_out.border_color = color_edge * float4(color_edge.aaa, 1.0f);
v_out.emboss_color = color_emboss * float4(color_emboss.aaa, 1.0f);
return v_out;
}
VertOut do_tria(int vert_id, float2 &pos)
{
VertOut v_out;
int vidx = vert_id % 4;
bool tria2 = vert_id > 7;
pos = float2(0.0f);
float size = (tria2) ? -tria2_size : tria1_size;
float2 center = (tria2) ? tria2_center : tria1_center;
float2 arrow_pos[] = {
float2(0.0f, 0.6f), float2(0.6f, 0.0f), float2(-0.6f, 0.0f), float2(0.0f, -0.6f)};
/* Rotated uv space by 45deg and mirrored. */
float2 arrow_uvs[] = {
float2(0.0f, 0.85f), float2(0.85f, 0.85f), float2(0.0f, 0.0f), float2(0.0f, 0.85f)};
float2 point_pos[] = {
float2(-1.0f, -1.0f), float2(-1.0f, 1.0f), float2(1.0f, -1.0f), float2(1.0f, 1.0f)};
float2 point_uvs[] = {
float2(0.0f, 0.0f), float2(0.0f, 1.0f), float2(1.0f, 0.0f), float2(1.0f, 1.0f)};
/* We reuse the SDF round-box rendering of widget to render the tria shapes.
* This means we do clever tricks to position the rectangle the way we want using
* the 2 triangles uvs. */
if (tria_type == 0.0f) {
/* ROUNDBOX_TRIA_NONE */
v_out.out_rect_size = v_out.uv_interp = pos = float2(0);
v_out.out_round_corners = float4(0.01f);
}
else if (tria_type == 1.0f) {
/* ROUNDBOX_TRIA_ARROWS */
pos = arrow_pos[vidx];
v_out.uv_interp = arrow_uvs[vidx];
v_out.uv_interp -= float2(0.05f, 0.63f); /* Translate */
v_out.out_rect_size = float2(0.74f, 0.17f);
v_out.out_round_corners = float4(0.08f);
}
else if (tria_type == 2.0f) {
/* ROUNDBOX_TRIA_SCROLL */
pos = point_pos[vidx];
v_out.uv_interp = point_uvs[vidx];
v_out.out_rect_size = float2(1.0f);
v_out.out_round_corners = float4(0.5f);
}
else if (tria_type == 3.0f) {
/* ROUNDBOX_TRIA_MENU */
pos = tria2 ? float2(0.0f) : arrow_pos[vidx]; /* Solo tria */
pos = float2(pos.y, -pos.x); /* Rotate */
pos += float2(-0.05f, 0.0f); /* Translate */
size *= 0.8f; /* Scale */
v_out.uv_interp = arrow_uvs[vidx];
v_out.uv_interp -= float2(0.05f, 0.63f); /* Translate */
v_out.out_rect_size = float2(0.74f, 0.17f);
v_out.out_round_corners = float4(0.01f);
}
else if (tria_type == 4.0f) {
/* ROUNDBOX_TRIA_CHECK */
/* A bit more hacky: We use the two triangles joined together to render
* both sides of the check-mark with different length. */
pos = arrow_pos[min(vidx, 2)]; /* Only keep 1 triangle. */
pos.y = tria2 ? -pos.y : pos.y; /* Mirror along X */
pos = pos.x * float2(0.0872f, -0.996f) +
pos.y * float2(0.996f, 0.0872f); /* Rotate (85deg) */
pos += float2(-0.1f, 0.2f); /* Translate */
center = tria1_center;
size = tria1_size * 1.7f; /* Scale */
v_out.uv_interp = arrow_uvs[vidx];
v_out.uv_interp -= tria2 ? float2(0.4f, 0.65f) : float2(0.08f, 0.65f); /* Translate */
v_out.out_rect_size = float2(0.74f, 0.14f);
v_out.out_round_corners = float4(0.01f);
}
else if (tria_type == 5.0f) {
/* ROUNDBOX_TRIA_HOLD_ACTION_ARROW */
/* We use a single triangle to cut the round rect in half.
* The edge will not be Anti-aliased. */
pos = tria2 ? float2(0.0f) : arrow_pos[min(vidx, 2)]; /* Only keep 1 triangle. */
pos = pos.x * float2(0.707f, 0.707f) + pos.y * float2(-0.707f, 0.707f); /* Rotate (45deg)
*/
pos += float2(-1.7f, 2.4f); /* Translate (hard-coded, might want to remove). */
size *= 0.4f; /* Scale */
v_out.uv_interp = arrow_uvs[vidx];
v_out.uv_interp -= float2(0.05f, 0.05f); /* Translate */
v_out.out_rect_size = float2(0.75f);
v_out.out_round_corners = float4(0.01f);
}
else if (tria_type == 6.0f) {
/* ROUNDBOX_TRIA_DASH */
pos = point_pos[vidx];
v_out.uv_interp = point_uvs[vidx];
v_out.uv_interp -= float2(0.2f, 0.45f); /* Translate */
v_out.out_rect_size = float2(0.6f, 0.1f);
v_out.out_round_corners = float4(0.01f);
}
v_out.uv_interp *= abs(size);
v_out.out_rect_size *= abs(size);
v_out.out_round_corners *= abs(size);
pos = pos * size + center;
v_out.inner_color = color_tria * float4(color_tria.aaa, 1.0f);
v_out.line_width = 0.0f;
v_out.border_color = float4(0.0f);
v_out.emboss_color = float4(0.0f);
v_out.but_co = -2.0f;
return v_out;
}
};
/* WORKAROUND: We cannot use structs with push constants, so we push a float4 array and reinterpret
* using a union. */
struct WidgetUnion {
union {
union_t<WidgetRaw> raw;
union_t<Widget> data;
};
};
struct Resources {
[[legacy_info]] ShaderCreateInfo gpu_srgb_to_framebuffer_space;
[[push_constant]] const float4x4 ModelViewProjectionMatrix;
[[push_constant]] const float3 checkerColorAndSize;
[[compilation_constant]] const bool instanced;
[[push_constant, condition(instanced)]] const float4 parameters_inst[MAX_PARAM * MAX_INSTANCE];
[[push_constant, condition(!instanced)]] const float4 parameters[MAX_PARAM];
/** Unpack widget data passed as raw array of float4 through push constants. */
Widget get_widget(int index)
{
/* Hopefully, all of these move instructions are optimized out. */
WidgetRaw raw;
if (this->instanced) [[static_branch]] {
for (int i = 0; i < 12; i++) [[unroll]] {
raw.data[i] = parameters_inst[index * MAX_PARAM + i];
}
}
else {
for (int i = 0; i < 12; i++) [[unroll]] {
raw.data[i] = parameters[i];
}
}
/* Equivalent of reinterpret_cast. */
WidgetUnion widget;
widget.raw() = raw;
return widget.data();
}
float4 do_checkerboard(float2 frag_co)
{
float size = checkerColorAndSize.z;
float2 phase = mod(frag_co.xy, size * 2.0f);
if ((phase.x > size && phase.y < size) || (phase.x < size && phase.y > size)) {
return float4(checkerColorAndSize.xxx, 1.0f);
}
return float4(checkerColorAndSize.yyy, 1.0f);
}
};
[[vertex]] void vert([[vertex_id]] const int vert_id,
[[instance_id]] const int inst_id,
[[resource_table]] Resources &srt,
[[out]] VertOut &v_out,
[[position]] float4 &position)
{
Widget widget = srt.get_widget(inst_id);
bool is_tria = (vert_id > 3);
float2 pos;
VertOut vert_out = (is_tria) ? widget.do_tria(vert_id, pos) : widget.do_widget(vert_id, pos);
/* WORKAROUND: Quirk of current BSL implementation.
* Current implementation doesn't allow to assign the output struct at once. */
v_out.discard_fac = widget.discard_factor();
v_out.line_width = vert_out.line_width;
v_out.out_rect_size = vert_out.out_rect_size;
v_out.border_color = vert_out.border_color;
v_out.emboss_color = vert_out.emboss_color;
v_out.out_round_corners = vert_out.out_round_corners;
v_out.but_co = vert_out.but_co;
v_out.uv_interp = vert_out.uv_interp;
v_out.inner_color = vert_out.inner_color;
position = srt.ModelViewProjectionMatrix * float4(pos, 0.0f, 1.0f);
}
struct FragOut {
[[frag_color(0)]] float4 color;
};
[[fragment]] void frag([[in]] const VertOut &v_out,
[[out]] FragOut &frag_out,
[[frag_coord]] const float4 frag_co,
[[resource_table]] Resources &srt)
{
if (min(1.0f, -v_out.but_co) > v_out.discard_fac) {
gpu_discard_fragment();
}
float2 uv = v_out.uv_interp;
bool upper_half = uv.y > v_out.out_rect_size.y * 0.5f;
bool right_half = uv.x > v_out.out_rect_size.x * 0.5f;
float corner_rad;
/* Correct aspect ratio for 2D views not using uniform scaling.
* uv is already in pixel space so a uniform scale should give us a ratio of 1. */
float ratio = (v_out.but_co != -2.0f) ? abs(gpu_dfdy(uv.y) / gpu_dfdx(uv.x)) : 1.0f;
float2 uv_sdf = uv;
uv_sdf.x *= ratio;
if (right_half) {
uv_sdf.x = v_out.out_rect_size.x * ratio - uv_sdf.x;
}
if (upper_half) {
uv_sdf.y = v_out.out_rect_size.y - uv_sdf.y;
corner_rad = right_half ? v_out.out_round_corners.z : v_out.out_round_corners.w;
}
else {
corner_rad = right_half ? v_out.out_round_corners.y : v_out.out_round_corners.x;
}
/* Fade emboss at the border. */
float emboss_size = upper_half ? 0.0f : min(1.0f, uv_sdf.x / (corner_rad * ratio));
/* Signed distance field from the corner (in pixel).
* inner_sdf is sharp and outer_sdf is rounded. */
uv_sdf -= corner_rad;
float inner_sdf = max(0.0f, min(uv_sdf.x, uv_sdf.y));
float outer_sdf = -length(min(uv_sdf, 0.0f));
float sdf = inner_sdf + outer_sdf + corner_rad;
/* Clamp line width to be at least 1px wide. This can happen if the projection matrix
* has been scaled (i.e: Node editor)... */
float line_width = (v_out.line_width > 0.0f) ? max(gpu_fwidth(uv.y), v_out.line_width) : 0.0f;
constexpr float aa_radius = 0.5f;
float3 masks;
masks.x = smoothstep(-aa_radius, aa_radius, sdf);
masks.y = smoothstep(-aa_radius, aa_radius, sdf - line_width);
masks.z = smoothstep(-aa_radius, aa_radius, sdf + line_width * emboss_size);
/* Compose masks together to avoid having too much alpha. */
masks.zx = max(float2(0.0f), masks.zx - masks.xy);
if (v_out.but_co > 0.0f) {
/* Alpha checker widget. */
if (v_out.but_co > 0.5f) {
float4 checker = srt.do_checkerboard(frag_co.xy);
frag_out.color = mix(checker, v_out.inner_color, v_out.inner_color.a);
}
else {
/* Set alpha to 1.0f. */
frag_out.color = v_out.inner_color;
}
frag_out.color.a = 1.0f;
}
else {
/* Pre-multiply here. */
frag_out.color = v_out.inner_color * float4(v_out.inner_color.aaa, 1.0f);
}
frag_out.color *= masks.y;
frag_out.color += masks.x * v_out.border_color;
frag_out.color += masks.z * v_out.emboss_color;
/* Un-pre-multiply because the blend equation is already doing the multiplication. */
if (frag_out.color.a > 0.0f) {
frag_out.color.rgb /= frag_out.color.a;
}
frag_out.color = blender_srgb_to_framebuffer_space(frag_out.color);
}
} // namespace builtin::widget
PipelineGraphic gpu_shader_2D_widget_base(builtin::widget::vert,
builtin::widget::frag,
builtin::widget::Resources{.instanced = false});
PipelineGraphic gpu_shader_2D_widget_base_inst(builtin::widget::vert,
builtin::widget::frag,
builtin::widget::Resources{.instanced = true});

View File

@@ -0,0 +1,17 @@
/* SPDX-FileCopyrightText: 2018-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_2D_widget_infos.hh"
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_2D_widget_shadow)
void main()
{
fragColor = float4(0.0f);
/* Manual curve fit of the falloff curve of previous drawing method. */
float shadow_alpha = alpha * (shadowFalloff * shadowFalloff * 0.722f + shadowFalloff * 0.277f);
float inner_alpha = smoothstep(0.0f, 0.05f, innerMask);
fragColor.a = inner_alpha * shadow_alpha;
}

View File

@@ -0,0 +1,130 @@
/* SPDX-FileCopyrightText: 2018-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_2D_widget_infos.hh"
VERTEX_SHADER_CREATE_INFO(gpu_shader_2D_widget_shadow)
#define BIT_RANGE(x) uint((1 << x) - 1)
/* 2 bits for corner */
/* Attention! Not the same order as in UI_interface.hh!
* Ordered by drawing order. */
#define BOTTOM_LEFT 0u
#define BOTTOM_RIGHT 1u
#define TOP_RIGHT 2u
#define TOP_LEFT 3u
#define CNR_FLAG_RANGE BIT_RANGE(2)
/* 4bits for corner id */
#define CORNER_VEC_OFS 2u
#define CORNER_VEC_RANGE BIT_RANGE(4)
#define INNER_FLAG uint(1 << 10) /* is inner vert */
/* Radii and rad per corner. */
#define recti parameters[0]
#define rect parameters[1]
#define radsi parameters[2].x
#define rads parameters[2].y
#define roundCorners parameters[3]
void main()
{
/* NOTE(Metal): Declaring constant array in function scope to avoid increasing local shader
* memory pressure. */
constexpr float2 cornervec[36] = float2_array(float2(0.0f, 1.0f),
float2(0.02f, 0.805f),
float2(0.067f, 0.617f),
float2(0.169f, 0.45f),
float2(0.293f, 0.293f),
float2(0.45f, 0.169f),
float2(0.617f, 0.076f),
float2(0.805f, 0.02f),
float2(1.0f, 0.0f),
float2(-1.0f, 0.0f),
float2(-0.805f, 0.02f),
float2(-0.617f, 0.067f),
float2(-0.45f, 0.169f),
float2(-0.293f, 0.293f),
float2(-0.169f, 0.45f),
float2(-0.076f, 0.617f),
float2(-0.02f, 0.805f),
float2(0.0f, 1.0f),
float2(0.0f, -1.0f),
float2(-0.02f, -0.805f),
float2(-0.067f, -0.617f),
float2(-0.169f, -0.45f),
float2(-0.293f, -0.293f),
float2(-0.45f, -0.169f),
float2(-0.617f, -0.076f),
float2(-0.805f, -0.02f),
float2(-1.0f, 0.0f),
float2(1.0f, 0.0f),
float2(0.805f, -0.02f),
float2(0.617f, -0.067f),
float2(0.45f, -0.169f),
float2(0.293f, -0.293f),
float2(0.169f, -0.45f),
float2(0.076f, -0.617f),
float2(0.02f, -0.805f),
float2(0.0f, -1.0f));
constexpr float2 center_offset[4] = float2_array(
float2(1.0f, 1.0f), float2(-1.0f, 1.0f), float2(-1.0f, -1.0f), float2(1.0f, -1.0f));
uint cflag = vflag & CNR_FLAG_RANGE;
uint vofs = (vflag >> CORNER_VEC_OFS) & CORNER_VEC_RANGE;
bool is_inner = (vflag & INNER_FLAG) != 0u;
float shadow_width = rads - radsi;
float shadow_width_top = rect.w - recti.w;
float rad_inner = radsi * roundCorners[cflag];
float rad_outer = rad_inner + shadow_width;
float radius = (is_inner) ? rad_inner : rad_outer;
float shadow_offset = (is_inner && (cflag > BOTTOM_RIGHT)) ? (shadow_width - shadow_width_top) :
0.0f;
float2 c = center_offset[cflag];
float2 center_outer = rad_outer * c;
float2 center = radius * c;
/* First expand all vertices to the outer shadow border. */
float2 v = rad_outer * cornervec[cflag * 9u + vofs];
/* Now shrink the inner vertices onto the inner rectangle.
* At the top corners we keep the vertical offset to distribute a few of the vertices along the
* straight part of the rectangle. This allows us to get a better falloff at the top. */
if (is_inner && (cflag > BOTTOM_RIGHT) && (v.y < (shadow_offset - rad_outer))) {
v.y += shadow_width_top;
v.x = 0.0f;
}
else {
v = radius * normalize(v - (center_outer + float2(0.0f, shadow_offset))) + center;
}
/* Position to corner */
float4 rct = (is_inner) ? recti : rect;
if (cflag == BOTTOM_LEFT) {
v += rct.xz;
}
else if (cflag == BOTTOM_RIGHT) {
v += rct.yz;
}
else if (cflag == TOP_RIGHT) {
v += rct.yw;
}
else /* (cflag == TOP_LEFT) */ {
v += rct.xw;
}
float inner_shadow_strength = min((rect.w - v.y) / rad_outer + 0.1f, 1.0f);
shadowFalloff = (is_inner) ? inner_shadow_strength : 0.0f;
innerMask = (is_inner) ? 0.0f : 1.0f;
gl_Position = ModelViewProjectionMatrix * float4(v, 0.0f, 1.0f);
}

View File

@@ -0,0 +1,13 @@
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_3D_uniform_color_infos.hh"
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_clipped_uniform_color)
void main()
{
gl_Position = ModelViewProjectionMatrix * float4(pos, 1.0f);
gl_ClipDistance[0] = dot(ModelMatrix * float4(pos, 1.0f), ClipPlane);
}

View File

@@ -0,0 +1,20 @@
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_3D_flat_color_infos.hh"
#include "gpu_shader_cfg_world_clip_lib.glsl"
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_flat_color)
void main()
{
float4 pos_4d = float4(pos, 1.0f);
gl_Position = ModelViewProjectionMatrix * pos_4d;
finalColor = color;
#ifdef USE_WORLD_CLIP_PLANES
world_clip_planes_calc_clip_distance((clipPlanes.ClipModelMatrix * pos_4d).xyz);
#endif
}

View File

@@ -0,0 +1,13 @@
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_3D_image_infos.hh"
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_image_common)
void main()
{
gl_Position = ModelViewProjectionMatrix * float4(pos.xyz, 1.0f);
texCoord_interp = texCoord;
}

View File

@@ -0,0 +1,26 @@
/* SPDX-FileCopyrightText: 2017-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Vertex Shader for dashed lines with 3D coordinates,
* with uniform multi-colors or uniform single-color, and unary thickness.
*
* Dashed is performed in screen space.
*/
#include "infos/gpu_shader_line_dashed_uniform_color_infos.hh"
#include "gpu_shader_cfg_world_clip_lib.glsl"
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_line_dashed_uniform_color_clipped)
void main()
{
float4 pos_4d = float4(pos, 1.0f);
gl_Position = ModelViewProjectionMatrix * pos_4d;
stipple_start = stipple_pos = viewport_size * 0.5f * (gl_Position.xy / gl_Position.w);
#ifdef USE_WORLD_CLIP_PLANES
world_clip_planes_calc_clip_distance((ModelMatrix * pos_4d).xyz);
#endif
}

View File

@@ -0,0 +1,13 @@
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_simple_lighting_infos.hh"
VERTEX_SHADER_CREATE_INFO(gpu_shader_simple_lighting)
void main()
{
normal = normalize(NormalMatrix * nor);
gl_Position = ModelViewProjectionMatrix * float4(pos, 1.0f);
}

View File

@@ -0,0 +1,21 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_3D_point_infos.hh"
#include "gpu_shader_cfg_world_clip_lib.glsl"
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_point_flat_color)
void main()
{
float4 pos_4d = float4(pos, 1.0f);
gl_Position = ModelViewProjectionMatrix * pos_4d;
gl_PointSize = size;
finalColor = color;
#ifdef USE_WORLD_CLIP_PLANES
world_clip_planes_calc_clip_distance((clipPlanes.ClipModelMatrix * pos_4d).xyz);
#endif
}

View File

@@ -0,0 +1,24 @@
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_3D_point_infos.hh"
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_point_uniform_size_uniform_color_aa)
void main()
{
float4 pos_4d = float4(pos, 1.0f);
gl_Position = ModelViewProjectionMatrix * pos_4d;
gl_PointSize = size;
/* Calculate concentric radii in pixels. */
float radius = 0.5f * size;
/* Start at the outside and progress toward the center. */
radii[0] = radius;
radii[1] = radius - 1.0f;
/* Convert to PointCoord units. */
radii /= size;
}

View File

@@ -0,0 +1,14 @@
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_3D_point_infos.hh"
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_point_varying_size_varying_color)
void main()
{
gl_Position = ModelViewProjectionMatrix * float4(pos, 1.0f);
gl_PointSize = size;
finalColor = color;
}

View File

@@ -0,0 +1,23 @@
/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_3D_polyline_infos.hh"
#include "gpu_shader_colorspace_lib.glsl"
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_3D_polyline_uniform_color)
void main()
{
#ifdef CLIP
if (clip < 0.0f) {
gpu_discard_fragment();
}
#endif
fragColor = final_color;
if (lineSmooth) {
fragColor.a *= clamp((lineWidth + SMOOTH_WIDTH) * 0.5f - abs(smoothline), 0.0f, 1.0f);
}
fragColor = blender_srgb_to_framebuffer_space(fragColor);
}

View File

@@ -0,0 +1,216 @@
/* SPDX-FileCopyrightText: 2020-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_3D_polyline_infos.hh"
#include "gpu_shader_attribute_load_lib.glsl"
#include "gpu_shader_index_load_lib.glsl"
#include "gpu_shader_math_base_lib.glsl"
#include "gpu_shader_utildefines_lib.glsl"
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_polyline_flat_color)
struct VertIn {
float3 ls_P;
float4 final_color;
};
VertIn input_assembly(uint in_vertex_id)
{
uint v_i = gpu_index_load(in_vertex_id);
uint ofs = uint(gpu_vert_stride_count_offset.z);
VertIn vert_in;
vert_in.ls_P = float3(0.0f, 0.0f, 0.0f);
/* Need to support 1, 2 and 3 dimensional input (sigh). */
vert_in.ls_P.x = pos[gpu_attr_load_index(v_i, gpu_attr_0) + 0 + ofs];
if (gpu_attr_0_len >= 2) {
vert_in.ls_P.y = pos[gpu_attr_load_index(v_i, gpu_attr_0) + 1 + ofs];
}
if (gpu_attr_0_len >= 3) {
vert_in.ls_P.z = pos[gpu_attr_load_index(v_i, gpu_attr_0) + 2 + ofs];
}
if (gpu_attr_0_fetch_int) {
vert_in.ls_P = float3(floatBitsToInt(vert_in.ls_P));
}
#ifndef UNIFORM
vert_in.final_color = float4(0.0f, 0.0f, 0.0f, 1.0f);
/* Need to support 1, 2, 3 and 4 dimensional input (sigh). */
vert_in.final_color.x = color[gpu_attr_load_index(v_i, gpu_attr_1) + 0 + ofs];
if (gpu_attr_1_fetch_unorm8) {
vert_in.final_color = unpackUnorm4x8(floatBitsToUint(vert_in.final_color.x));
}
else {
if (gpu_attr_1_len >= 2) {
vert_in.final_color.y = color[gpu_attr_load_index(v_i, gpu_attr_1) + 1 + ofs];
}
if (gpu_attr_1_len >= 3) {
vert_in.final_color.z = color[gpu_attr_load_index(v_i, gpu_attr_1) + 2 + ofs];
}
if (gpu_attr_1_len >= 4) {
vert_in.final_color.w = color[gpu_attr_load_index(v_i, gpu_attr_1) + 3 + ofs];
}
}
#endif
return vert_in;
}
struct VertOut {
float4 gpu_position;
float4 final_color;
float clip;
};
VertOut vertex_main(VertIn vert_in)
{
VertOut vert_out;
vert_out.gpu_position = ModelViewProjectionMatrix * float4(vert_in.ls_P, 1.0f);
#ifndef UNIFORM
vert_out.final_color = vert_in.final_color;
#endif
#ifdef CLIP
vert_out.clip = dot(ModelMatrix * float4(vert_in.ls_P, 1.0f), ClipPlane);
#endif
return vert_out;
}
/* Clips point to near clip plane before perspective divide. */
float4 clip_line_point_homogeneous_space(float4 p, float4 q)
{
if (p.z < -p.w) {
/* Just solves p + (q - p) * A; for A when p.z / p.w = -1.0f. */
float denom = q.z - p.z + q.w - p.w;
if (denom == 0.0f) {
/* No solution. */
return p;
}
float A = (-p.z - p.w) / denom;
p = p + (q - p) * A;
}
return p;
}
struct GeomOut {
float4 gpu_position;
float4 final_color;
float clip;
float smoothline;
};
void export_vertex(GeomOut geom_out)
{
gl_Position = geom_out.gpu_position;
final_color = geom_out.final_color;
smoothline = geom_out.smoothline;
clip = geom_out.clip;
}
void strip_EmitVertex(const uint strip_index,
uint out_vertex_id,
uint out_primitive_id,
GeomOut geom_out)
{
bool is_odd_primitive = (out_primitive_id & 1u) != 0u;
/* Maps triangle list primitives to triangle strip indices. */
uint out_strip_index = (is_odd_primitive ? (2u - out_vertex_id) : out_vertex_id) +
out_primitive_id;
if (out_strip_index == strip_index) {
export_vertex(geom_out);
}
}
void do_vertex(const uint i,
uint out_vertex_id,
uint out_primitive_id,
VertOut geom_in[2],
float4 position,
float2 ofs)
{
GeomOut geom_out;
#if defined(UNIFORM)
geom_out.final_color = color;
#elif defined(FLAT)
/* WATCH: Assuming last provoking vertex. */
geom_out.final_color = geom_in[1].final_color;
#elif defined(SMOOTH)
geom_out.final_color = geom_in[i].final_color;
#endif
#ifdef CLIP
geom_out.clip = geom_in[i].clip;
#endif
geom_out.smoothline = (lineWidth + SMOOTH_WIDTH * float(lineSmooth)) * 0.5f;
geom_out.gpu_position = position;
geom_out.gpu_position.xy += ofs * position.w;
strip_EmitVertex(i * 2u + 0u, out_vertex_id, out_primitive_id, geom_out);
geom_out.smoothline = -(lineWidth + SMOOTH_WIDTH * float(lineSmooth)) * 0.5f;
geom_out.gpu_position = position;
geom_out.gpu_position.xy -= ofs * position.w;
strip_EmitVertex(i * 2u + 1u, out_vertex_id, out_primitive_id, geom_out);
}
void geometry_main(VertOut geom_in[2],
uint out_vertex_id,
uint out_primitive_id,
uint /*out_invocation_id*/)
{
float4 p0 = clip_line_point_homogeneous_space(geom_in[0].gpu_position, geom_in[1].gpu_position);
float4 p1 = clip_line_point_homogeneous_space(geom_in[1].gpu_position, geom_in[0].gpu_position);
float2 e = normalize(((p1.xy / p1.w) - (p0.xy / p0.w)) * viewportSize.xy);
#if 0 /* Hard turn when line direction changes quadrant. */
e = abs(e);
float2 ofs = (e.x > e.y) ? float2(0.0f, 1.0f / e.x) : float2(1.0f / e.y, 0.0f);
#else /* Use perpendicular direction. */
float2 ofs = float2(-e.y, e.x);
#endif
ofs /= viewportSize.xy;
ofs *= lineWidth + SMOOTH_WIDTH * float(lineSmooth);
do_vertex(0u, out_vertex_id, out_primitive_id, geom_in, p0, ofs);
do_vertex(1u, out_vertex_id, out_primitive_id, geom_in, p1, ofs);
}
void main()
{
/* Line list primitive. */
uint input_primitive_vertex_count = uint(gpu_vert_stride_count_offset.x);
/* Triangle list primitive (emulating triangle strip). */
constexpr uint output_primitive_vertex_count = 3u;
constexpr uint output_primitive_count = 2u;
constexpr uint output_invocation_count = 1u;
constexpr uint output_vertex_count_per_invocation = output_primitive_count *
output_primitive_vertex_count;
constexpr uint output_vertex_count_per_input_primitive = output_vertex_count_per_invocation *
output_invocation_count;
uint in_primitive_id = uint(gl_VertexID) / output_vertex_count_per_input_primitive;
uint in_primitive_first_vertex = in_primitive_id * input_primitive_vertex_count;
uint out_vertex_id = uint(gl_VertexID) % output_primitive_vertex_count;
uint out_primitive_id = (uint(gl_VertexID) / output_primitive_vertex_count) %
output_primitive_count;
uint out_invocation_id = (uint(gl_VertexID) / output_vertex_count_per_invocation) %
output_invocation_count;
/* Used to wrap around for the line loop case. */
uint input_total_vertex_count = uint(gpu_vert_stride_count_offset.y);
VertIn vert_in[2];
vert_in[0] = input_assembly(in_primitive_first_vertex + 0u);
vert_in[1] = input_assembly((in_primitive_first_vertex + 1u) % input_total_vertex_count);
VertOut vert_out[2];
vert_out[0] = vertex_main(vert_in[0]);
vert_out[1] = vertex_main(vert_in[1]);
/* Discard by default. */
gl_Position = float4(NAN_FLT);
geometry_main(vert_out, out_vertex_id, out_primitive_id, out_invocation_id);
}

View File

@@ -0,0 +1,15 @@
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_3D_smooth_color_infos.hh"
#include "gpu_shader_colorspace_lib.glsl"
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_3D_smooth_color)
void main()
{
fragColor = finalColor;
fragColor = blender_srgb_to_framebuffer_space(fragColor);
}

View File

@@ -0,0 +1,19 @@
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_3D_smooth_color_infos.hh"
#include "gpu_shader_cfg_world_clip_lib.glsl"
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_smooth_color)
void main()
{
gl_Position = ModelViewProjectionMatrix * float4(pos, 1.0f);
finalColor = color;
#ifdef USE_WORLD_CLIP_PLANES
world_clip_planes_calc_clip_distance((clipPlanes.ClipModelMatrix * float4(pos, 1.0f)).xyz);
#endif
}

View File

@@ -0,0 +1,18 @@
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_3D_depth_only_infos.hh"
#include "gpu_shader_cfg_world_clip_lib.glsl"
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_depth_only)
void main()
{
gl_Position = ModelViewProjectionMatrix * float4(pos, 1.0f);
#ifdef USE_WORLD_CLIP_PLANES
world_clip_planes_calc_clip_distance((clipPlanes.ClipModelMatrix * float4(pos, 1.0f)).xyz);
#endif
}

View File

@@ -0,0 +1,29 @@
/* SPDX-FileCopyrightText: 2019-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "infos/gpu_clip_planes_infos.hh"
#ifdef GPU_FRAGMENT_SHADER
# error File should not be included in fragment shader
#endif
#ifdef USE_WORLD_CLIP_PLANES
VERTEX_SHADER_CREATE_INFO(gpu_clip_planes)
void world_clip_planes_calc_clip_distance(float3 wpos)
{
float4 pos = float4(wpos, 1.0f);
gl_ClipDistance[0] = dot(clipPlanes.world[0], pos);
gl_ClipDistance[1] = dot(clipPlanes.world[1], pos);
gl_ClipDistance[2] = dot(clipPlanes.world[2], pos);
gl_ClipDistance[3] = dot(clipPlanes.world[3], pos);
gl_ClipDistance[4] = dot(clipPlanes.world[4], pos);
gl_ClipDistance[5] = dot(clipPlanes.world[5], pos);
}
#endif

View File

@@ -0,0 +1,19 @@
/* SPDX-FileCopyrightText: 2017-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_2D_checker_infos.hh"
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_2D_checker)
void main()
{
float2 phase = mod(gl_FragCoord.xy, (size * 2));
if ((phase.x > size && phase.y < size) || (phase.x < size && phase.y > size)) {
fragColor = color1;
}
else {
fragColor = color2;
}
}

View File

@@ -0,0 +1,326 @@
/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_compat.hh"
float3 calc_barycentric_distances(float3 pos0, float3 pos1, float3 pos2)
{
float3 edge21 = pos2 - pos1;
float3 edge10 = pos1 - pos0;
float3 edge02 = pos0 - pos2;
float3 d21 = normalize(edge21);
float3 d10 = normalize(edge10);
float3 d02 = normalize(edge02);
float3 dists;
float d = dot(d21, edge02);
dists.x = sqrt(dot(edge02, edge02) - d * d);
d = dot(d02, edge10);
dists.y = sqrt(dot(edge10, edge10) - d * d);
d = dot(d10, edge21);
dists.z = sqrt(dot(edge21, edge21) - d * d);
return dists;
}
float2 calc_barycentric_co(int vertid)
{
float2 bary;
bary.x = float((vertid % 3) == 0);
bary.y = float((vertid % 3) == 1);
return bary;
}
/* Assumes GPU_VEC4 is color data, special case that needs luminance coefficients from OCIO. */
#define float_from_float4(v, luminance_coefficients) dot(v.rgb, luminance_coefficients)
#define float_from_float3(v) ((v.r + v.g + v.b) * (1.0f / 3.0f))
#define float_from_float2(v) ((v.x + v.y) * (1.0f / 2.0f))
#define float2_from_float4(v) v.xy
#define float2_from_float3(v) v.xy
#define float2_from_float(v) float2(v)
#define float3_from_float4(v) v.rgb
#define float3_from_float2(v) float3(v.xy, 0.0f)
#define float3_from_float(v) float3(v)
#define float4_from_float3(v) float4(v, 1.0f)
#define float4_from_float2(v) float4(v.xy, 0.0f, 1.0f)
#define float4_from_float(v) float4(float3(v), 1.0f)
#ifdef GPU_FRAGMENT_SHADER
# define FrontFacing gl_FrontFacing
#else
# define FrontFacing true
#endif
enum ClosureType : uchar {
CLOSURE_NONE_ID = 0u,
/* Diffuse */
CLOSURE_BSDF_DIFFUSE_ID = 1u,
// CLOSURE_BSDF_OREN_NAYAR_ID = 2u, /* TODO */
// CLOSURE_BSDF_SHEEN_ID = 4u, /* TODO */
// CLOSURE_BSDF_DIFFUSE_TOON_ID = 5u, /* TODO */
CLOSURE_BSDF_TRANSLUCENT_ID = 6u,
/* Glossy */
CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID = 7u,
// CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID = 8u, /* TODO */
// CLOSURE_BSDF_ASHIKHMIN_VELVET_ID = 9u, /* TODO */
// CLOSURE_BSDF_GLOSSY_TOON_ID = 10u, /* TODO */
// CLOSURE_BSDF_HAIR_REFLECTION_ID = 11u, /* TODO */
/* Transmission */
CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID = 12u,
CLOSURE_BSDF_THIN_GLASS_TRANSMISSION_ID = 13u,
/* Glass */
// CLOSURE_BSDF_HAIR_HUANG_ID = 14u, /* TODO */
/* BSSRDF */
CLOSURE_BSSRDF_BURLEY_ID = 15u,
};
struct ClosureUndetermined {
packed_float3 color;
float weight;
packed_float3 N;
ClosureType type;
/* Additional data different for each closure type. */
packed_float4 data;
};
bool closure_has_transmission(const ClosureType closure)
{
return closure == CLOSURE_BSDF_TRANSLUCENT_ID ||
closure == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID ||
closure == CLOSURE_BSDF_THIN_GLASS_TRANSMISSION_ID;
}
ClosureUndetermined closure_new(ClosureType type)
{
ClosureUndetermined cl;
cl.type = type;
return cl;
}
struct ClosureOcclusion {
packed_float3 N;
};
struct ClosureDiffuse {
packed_float3 color;
float weight;
packed_float3 N;
};
struct ClosureSubsurface {
packed_float3 color;
float weight;
packed_float3 N;
packed_float3 sss_radius;
};
struct ClosureTranslucent {
packed_float3 color;
float weight;
packed_float3 N;
};
struct ClosureReflection {
packed_float3 color;
float weight;
packed_float3 N;
float roughness;
};
struct ClosureRefraction {
packed_float3 color;
float weight;
packed_float3 N;
float roughness;
float ior;
};
struct ClosureHair {
packed_float3 color;
float weight;
packed_float3 T;
float offset;
packed_float2 roughness;
};
struct ClosureVolumeScatter {
packed_float3 scattering;
float weight;
float anisotropy;
};
struct ClosureVolumeAbsorption {
packed_float3 absorption;
float weight;
};
struct ClosureEmission {
packed_float3 emission;
float weight;
};
struct ClosureTransparency {
packed_float3 transmittance;
float weight;
float holdout;
};
struct ClosureThinRefraction {
packed_float3 color;
float weight;
packed_float3 N;
float roughness;
};
ClosureDiffuse to_closure_diffuse(ClosureUndetermined cl)
{
ClosureDiffuse closure;
closure.N = cl.N;
closure.color = cl.color;
return closure;
}
ClosureSubsurface to_closure_subsurface(ClosureUndetermined cl)
{
ClosureSubsurface closure;
closure.N = cl.N;
closure.color = cl.color;
closure.sss_radius = cl.data.xyz;
return closure;
}
ClosureTranslucent to_closure_translucent(ClosureUndetermined cl)
{
ClosureTranslucent closure;
closure.N = cl.N;
closure.color = cl.color;
return closure;
}
ClosureReflection to_closure_reflection(ClosureUndetermined cl)
{
ClosureReflection closure;
closure.N = cl.N;
closure.color = cl.color;
closure.roughness = cl.data.x;
return closure;
}
ClosureRefraction to_closure_refraction(ClosureUndetermined cl)
{
ClosureRefraction closure;
closure.N = cl.N;
closure.color = cl.color;
closure.roughness = cl.data.x;
closure.ior = cl.data.y;
return closure;
}
ClosureThinRefraction to_closure_thin_refraction(ClosureUndetermined cl)
{
ClosureThinRefraction closure;
closure.N = cl.N;
closure.color = cl.color;
closure.roughness = cl.data.x;
return closure;
}
struct GlobalData {
/** World position. */
packed_float3 P;
/** Surface Normal. Normalized, overridden by bump displacement. */
packed_float3 N;
/** Raw interpolated normal (non-normalized) data. */
packed_float3 Ni;
/** Geometric Normal. */
packed_float3 Ng;
/** Curve Tangent Space. */
packed_float3 curve_T, curve_B, curve_N;
/** Barycentric coordinates. */
packed_float2 barycentric_coords;
packed_float3 barycentric_dists;
/** Hair thickness in world space. */
float hair_diameter;
/** Index of the strand for per strand effects. */
int hair_strand_id;
/** Ray properties (approximation). */
float ray_depth;
float ray_length;
uchar ray_type;
/** Is hair. */
bool is_strand;
};
GlobalData g_data;
#ifndef GPU_FRAGMENT_SHADER
/* Stubs. */
# define dF_impl(a) (float3(0.0f))
# define dF_branch(a, b, c) (c = float2(0.0f))
# define dF_branch_incomplete(a, b, c) (c = float2(0.0f))
#elif defined(GPU_FAST_DERIVATIVE) /* TODO(@fclem): User Option? */
/* Fast derivatives */
float3 dF_impl(float3 v)
{
return float3(0.0f);
}
void dF_branch(float fn, float2 &result)
{
/* NOTE: this function is currently unused, once it is used we need to check if
* `g_derivative_filter_width` needs to be applied. */
result.x = gpu_dfdx(fn) * derivative_scale_get();
result.y = gpu_dfdy(fn) * derivative_scale_get();
}
#else
/* Offset of coordinates for evaluating bump node. Unit in pixel. */
float g_derivative_filter_width = 0.0f;
/* Precise derivatives */
int g_derivative_flag = 0;
float3 dF_impl(float3 v)
{
if (g_derivative_flag > 0) {
return gpu_dfdx(v) * g_derivative_filter_width;
}
else if (g_derivative_flag < 0) {
return gpu_dfdy(v) * g_derivative_filter_width;
}
return float3(0.0f);
}
# define dF_branch(fn, filter_width, result) \
if (true) { \
g_derivative_filter_width = filter_width * derivative_scale_get(); \
g_derivative_flag = 1; \
result.x = (fn); \
g_derivative_flag = -1; \
result.y = (fn); \
g_derivative_flag = 0; \
result -= float2((fn)); \
}
/* Used when the non-offset value is already computed elsewhere */
# define dF_branch_incomplete(fn, filter_width, result) \
if (true) { \
g_derivative_filter_width = filter_width * derivative_scale_get(); \
g_derivative_flag = 1; \
result.x = (fn); \
g_derivative_flag = -1; \
result.y = (fn); \
g_derivative_flag = 0; \
}
#endif

View File

@@ -0,0 +1,80 @@
/* SPDX-FileCopyrightText: 2020-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
*/
#pragma once
#include "infos/gpu_srgb_to_framebuffer_space_infos.hh"
SHADER_LIBRARY_CREATE_INFO(gpu_srgb_to_framebuffer_space)
/* Undefine the macro that avoids compilation errors. */
#undef blender_srgb_to_framebuffer_space
/**
* Input is Rec.709 sRGB.
* Output is Rec.709 linear if hardware will add a Linear to sRGB conversion, NOOP otherwise.
* NOTE: Old naming convention, but avoids breaking compatibility for python shaders.
*
* As per GPU API design, all frame-buffers with SRGBA_8_8_8_8 attachments will always enable SRGB
* rendering. In this mode the shader output is expected to be in a linear color space. This allows
* to do the blending stage with linear values (more correct) and then store the result in 8bpc
* keeping accurate colors.
*
* To ensure consistent result (blending excluded) between a frame-buffer using SRGBA_8_8_8_8 and
* one using RGBA_8_8_8_8, we need to do the sRGB > linear conversion to counteract the hardware
* encoding during frame-buffer output.
*
* For reference: https://wikis.khronos.org/opengl/framebuffer#Colorspace
*/
float4 blender_srgb_to_framebuffer_space(float4 srgb_color)
{
/**
* IMPORTANT: srgbTarget denote that the output is expected to be in __linear__ space.
* https://wikis.khronos.org/opengl/framebuffer#Colorspace
*/
if (!srgbTarget) {
/* Input should already be in sRGB. */
return srgb_color;
}
/* Note that this is simply counteracting the hardware Linear > sRGB conversion. */
float3 c = max(srgb_color.rgb, float3(0.0f));
float3 c1 = c * (1.0f / 12.92f);
float3 c2 = pow((c + 0.055f) * (1.0f / 1.055f), float3(2.4f));
float4 linear_color;
linear_color.rgb = mix(c1, c2, step(float3(0.04045f), c));
linear_color.a = srgb_color.a;
return linear_color;
}
/**
* Input is Rec.709 sRGB.
* Output is Rec.709 linear if hardware will add a Linear to sRGB conversion, NOOP otherwise.
*/
float4 blender_rec709_srgb_to_output_space(float4 srgb_color)
{
return blender_srgb_to_framebuffer_space(srgb_color);
}
/* Input is Blender Scene Linear. Output is Rec.709 sRGB. */
float4 blender_scene_linear_to_rec709_srgb(float3x3 scene_linear_to_rec709,
float4 scene_linear_color)
{
float3 rec709_linear = scene_linear_to_rec709 * scene_linear_color.rgb;
/* TODO(fclem): For wide gamut (extended sRGB), we need to encode negative values in a certain
* way here. */
/* Linear to sRGB transform. */
float3 c = max(rec709_linear, float3(0.0f));
float3 c1 = c * 12.92f;
float3 c2 = 1.055f * pow(c, float3(1.0f / 2.4f)) - 0.055f;
float4 srgb_color;
srgb_color.rgb = mix(c1, c2, step(float3(0.0031308f), c));
srgb_color.a = scene_linear_color.a;
return srgb_color;
}

View File

@@ -0,0 +1,18 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Implementation of basic shading language functions (types, builtin functions, etc...).
*
* Each backend will replace it with its own implementation at runtime.
*/
#pragma once
/* This file must replaced at runtime. The following content is only a possible implementation. */
#pragma runtime_generated
#include "gpu_shader_compat_cxx.hh" // IWYU pragma: export
/* Other possible implementation. */
// #include "gpu_shader_compat_glsl.hh"
// #include "gpu_shader_compat_msl.hh"

View File

@@ -0,0 +1,305 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Shading language to C++ stubs.
*
* The goal of this header is to make the Shading Language source file compile using a modern C++
* compiler. This allows for linting and IDE functionalities to work.
*
* This file can be included inside any Shading Language file to make the Shading Language syntax
* to work. Then your IDE must to be configured to associate `.glsl` files to C++ so that the C++
* linter does the analysis.
*
* This is why the implementation of each function is not needed. However, we make sure that type
* casting is always explicit. This is because implicit casts are not always supported on all
* implementations.
*
* Some of the features of Shading Language are omitted by design. They are either:
* - Not needed (e.g. per component matrix multiplication).
* - Against our code-style (e.g. `stpq` swizzle).
* - Unsupported by our Metal Shading Language layer (e.g. mixed vector-scalar matrix constructor).
*
* IMPORTANT: Please ask the module team if you need some feature that are not listed in this file.
*/
#pragma once
#include <cstdio> // IWYU pragma: export printf
#include "gpu_shader_cxx_builtin.hh" // IWYU pragma: export
#include "gpu_shader_cxx_global.hh" // IWYU pragma: export
#include "gpu_shader_cxx_image.hh" // IWYU pragma: export
#include "gpu_shader_cxx_matrix.hh" // IWYU pragma: export
#include "gpu_shader_cxx_sampler.hh" // IWYU pragma: export
#include "gpu_shader_cxx_string.hh" // IWYU pragma: export
#include "gpu_shader_cxx_vector.hh" // IWYU pragma: export
#define assert(assertion)
#include "gpu_shader_cxx_attribute.hh" // IWYU pragma: export
/* -------------------------------------------------------------------- */
/** \name Keywords
* \{ */
/* Decorate a variable in global scope that is common to all threads in a thread-group. */
#define shared
/** \} */
/* -------------------------------------------------------------------- */
/** \name Compatibility
* \{ */
/**
* Member hiding type.
* Wrapper type for members of unions in host shared structure.
* This is needed to force the accessor syntax in the shader code.
*/
template<typename T> struct union_t {
char bytes[sizeof(T)];
const T &operator()() const
{
return *reinterpret_cast<const T *>(&bytes);
}
T &operator()()
{
return *reinterpret_cast<T *>(&bytes);
}
};
/* Array syntax compatibility. */
/* clang-format off */
#define float_array(...) { __VA_ARGS__ }
#define float2_array(...) { __VA_ARGS__ }
#define float3_array(...) { __VA_ARGS__ }
#define float4_array(...) { __VA_ARGS__ }
#define int_array(...) { __VA_ARGS__ }
#define int2_array(...) { __VA_ARGS__ }
#define int3_array(...) { __VA_ARGS__ }
#define int4_array(...) { __VA_ARGS__ }
#define uint_array(...) { __VA_ARGS__ }
#define uint2_array(...) { __VA_ARGS__ }
#define uint3_array(...) { __VA_ARGS__ }
#define uint4_array(...) { __VA_ARGS__ }
#define bool_array(...) { __VA_ARGS__ }
#define bool2_array(...) { __VA_ARGS__ }
#define bool3_array(...) { __VA_ARGS__ }
#define bool4_array(...) { __VA_ARGS__ }
/* clang-format on */
/** \} */
/* Use to suppress `-Wimplicit-fallthrough` (in place of `break`). */
#ifndef ATTR_FALLTHROUGH
# ifdef __GNUC__
# define ATTR_FALLTHROUGH __attribute__((fallthrough))
# else
# define ATTR_FALLTHROUGH ((void)0)
# endif
#endif
/* GLSL main function must return void. C++ need to return int.
* Inject real main (C++) inside the GLSL main definition. */
#define main() \
/* Fake main prototype. */ \
/* void */ _fake_main(); \
/* Real main. */ \
int main() \
{ \
_fake_main(); \
return 0; \
} \
/* Fake main definition. */ \
void _fake_main()
#define GLSL_CPP_STUBS
#ifndef GPU_SHADER
# define GPU_SHADER
#endif
/* Reserved keywords in GLSL that are allowed in preprocessor directives for compiling in C++. */
#define sizeof static_assert(false, "sizeof is a reserved keyword")
#ifdef GPU_SHADER_LIBRARY
# define GPU_VERTEX_SHADER
# define GPU_FRAGMENT_SHADER
# define GPU_COMPUTE_SHADER
#endif
/* Resource accessor. */
#define specialization_constant_get(create_info, _res) create_info::_res
#define shared_variable_get(create_info, _res) create_info::_res
#define push_constant_get(create_info, _res) create_info::_res
#define interface_get(create_info, _res) create_info::_res
#define attribute_get(create_info, _res) create_info::_res
#define buffer_get(create_info, _res) create_info::_res
#define sampler_get(create_info, _res) create_info::_res
#define image_get(create_info, _res) create_info::_res
#define srt_access(create_info, _res) create_info::_res
/**
* WORKAROUND(fclem): Only used for cases when passing down the resource_table is impractical.
* Note that this placeholder is just for the code to compile.
*/
#define resource_table_get(table_type) (*(table_type *)(new char[1024 * 16]))
/**
* Member hiding type.
* Allows to declare fake references to Shader Resource Tables.
* This make sure we cannot directly reference them.
* This is just a safety measure for our fragile SRT implementation which cannot safely directly
* access SRT members that are more that 1 level deep.
* This should only be used in SRT struct member declaration for wrapping other SRT types.
*/
template<typename T> struct srt_t {
operator const T &() const
{
return *reinterpret_cast<const T *>(this);
}
operator T &()
{
return *reinterpret_cast<T *>(this);
}
};
struct ShaderCreateInfo {};
struct NoConstants {};
template<typename VertFn,
typename FragFn,
typename ConstT1 = NoConstants,
typename ConstT2 = NoConstants,
typename ConstT3 = NoConstants>
struct PipelineGraphic {
VertFn vert;
FragFn frag;
/* Constant values. */
ConstT1 c1;
ConstT2 c2;
ConstT3 c3;
PipelineGraphic(VertFn vert, FragFn frag) : vert(vert), frag(frag), c1({}), c2({}), c3({}) {}
PipelineGraphic(VertFn vert, FragFn frag, ConstT1 c1)
: vert(vert), frag(frag), c1(c1), c2({}), c3({})
{
}
PipelineGraphic(VertFn vert, FragFn frag, ConstT1 c1, ConstT2 c2)
: vert(vert), frag(frag), c1(c1), c2(c2), c3({})
{
}
PipelineGraphic(VertFn vert, FragFn frag, ConstT1 c1, ConstT2 c2, ConstT3 c3)
: vert(vert), frag(frag), c1(c1), c2(c2), c3(c3)
{
}
};
/* For assert support. */
#if defined(GPU_VERTEX_SHADER)
# define GPU_THREAD uint3(0)
#elif defined(GPU_FRAGMENT_SHADER)
# define GPU_THREAD uint3(0)
#elif defined(GPU_COMPUTE_SHADER)
# define GPU_THREAD uint3(0)
#else
# define GPU_THREAD error_not_in_a_shader_question_mark
#endif
template<typename CompFn,
typename ConstT1 = NoConstants,
typename ConstT2 = NoConstants,
typename ConstT3 = NoConstants>
struct PipelineCompute {
CompFn comp;
/* Constant values. */
ConstT1 c1;
ConstT2 c2;
ConstT3 c3;
PipelineCompute(CompFn comp) : comp(comp), c1({}), c2({}), c3({}) {}
PipelineCompute(CompFn comp, ConstT1 c1) : comp(comp), c1(c1), c2({}), c3({}) {}
PipelineCompute(CompFn comp, ConstT1 c1, ConstT2 c2) : comp(comp), c1(c1), c2(c2), c3({}) {}
PipelineCompute(CompFn comp, ConstT1 c1, ConstT2 c2, ConstT3 c3)
: comp(comp), c1(c1), c2(c2), c3(c3)
{
}
};
#include "GPU_shader_shared_utils.hh"
/* -------------------------------------------------------------------- */
/** \name Enums
*
* Enums should be defined in the root namespace when used directly in the pipeline, as they will
* not be fully qualified when generating the template name substitution. Defining in the root
* works around this limitation
*
* \{ */
/**
* TextureWriteFormat.
*
* We can not use GPU_TEXTURE_WRITE_FORMAT_EXPAND as other parts are included that will intervene
* with the compatibility defines.
*/
enum TextureWriteFormat : uint32_t {
SNORM_8,
SNORM_8_8,
SNORM_8_8_8_8,
SNORM_16,
SNORM_16_16,
SNORM_16_16_16_16,
UNORM_8,
UNORM_8_8,
UNORM_8_8_8_8,
UNORM_16,
UNORM_16_16,
UNORM_16_16_16_16,
SINT_8,
SINT_8_8,
SINT_8_8_8_8,
SINT_16,
SINT_16_16,
SINT_16_16_16_16,
SINT_32,
SINT_32_32,
SINT_32_32_32_32,
UINT_8,
UINT_8_8,
UINT_8_8_8_8,
UINT_16,
UINT_16_16,
UINT_16_16_16_16,
UINT_32,
UINT_32_32,
UINT_32_32_32_32,
SFLOAT_16,
SFLOAT_16_16,
SFLOAT_16_16_16_16,
SFLOAT_32,
SFLOAT_32_32,
SFLOAT_32_32_32_32,
UNORM_10_10_10_2,
UINT_10_10_10_2,
UFLOAT_11_11_10,
};
/** \} */

View File

@@ -0,0 +1,252 @@
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/* Do not apply C++ processing to this file. */
#pragma no_processing
#include "gpu_shader_glsl_extension.glsl"
/** Type aliases. */
/** IMPORTANT: Be wary of size and alignment matching for types that are present
* in C++ shared code. */
/* Matrix reshaping functions. Needs to be declared before matrix type aliases. */
#define RESHAPE(name, mat_to, mat_from) \
mat_to to_##name(mat_from m) \
{ \
return mat_to(m); \
}
/* clang-format off */
RESHAPE(float2x2, mat2x2, mat3x3)
RESHAPE(float2x2, mat2x2, mat4x4)
RESHAPE(float3x3, mat3x3, mat4x4)
RESHAPE(float3x3, mat3x3, mat2x2)
RESHAPE(float4x4, mat4x4, mat2x2)
RESHAPE(float4x4, mat4x4, mat3x3)
/* clang-format on */
/* TODO(fclem): Remove. Use Transform instead. */
RESHAPE(float3x3, mat3x3, mat3x4)
#undef RESHAPE
/* constexpr is equivalent to const in GLSL + special chaining rules.
* See "GLSL Specification section 4.3.3. Constant Expressions". */
#define constexpr const
/* Boolean in GLSL are 32bit in interface structs. */
#define bool32_t bool
#define bool2 bvec2
#define bool3 bvec3
#define bool4 bvec4
#define float2 vec2
#define float3 vec3
#define float4 vec4
#define int2 ivec2
#define int3 ivec3
#define int4 ivec4
#define uint2 uvec2
#define uint3 uvec3
#define uint4 uvec4
/* GLSL already follows the packed alignment / size rules for vec3. */
#define packed_float2 float2
#define packed_int2 int2
#define packed_uint2 uint2
#define packed_float3 float3
#define packed_int3 int3
#define packed_uint3 uint3
#define packed_float4 float4
#define packed_int4 int4
#define packed_uint4 uint4
#define float2x2 mat2x2
#define float3x2 mat3x2
#define float4x2 mat4x2
#define float2x3 mat2x3
#define float3x3 mat3x3
#define float4x3 mat4x3
#define float2x4 mat2x4
#define float3x4 mat3x4
#define float4x4 mat4x4
/* Small types are unavailable in GLSL (or badly supported), promote them to bigger type. */
#define char int
#define char2 int2
#define char3 int3
#define char4 int4
#define short int
#define short2 int2
#define short3 int3
#define short4 int4
#define uchar uint
#define uchar2 uint2
#define uchar3 uint3
#define uchar4 uint4
#define ushort uint
#define ushort2 uint2
#define ushort3 uint3
#define ushort4 uint4
#define half float
#define half2 float2
#define half3 float3
#define half4 float4
/* Aliases for supported fixed width types. */
#define int32_t int
#define uint32_t uint
/* Fast load/store variant macro. In GLSL this is the same as imageLoad/imageStore, but assumes no
* bounds checking. */
#define imageStoreFast imageStore
#define imageLoadFast imageLoad
/* Texture format tokens -- Type explicitness required by other Graphics APIs. */
#define sampler2DDepth sampler2D
#define sampler2DArrayDepth sampler2DArray
#define samplerCubeDepth sampler2D
#define samplerCubeArrayDepth sampler2DArray
#define usampler2DArrayAtomic usampler2DArray
#define usampler2DAtomic usampler2D
#define usampler3DAtomic usampler3D
#define isampler2DArrayAtomic isampler2DArray
#define isampler2DAtomic isampler2D
#define isampler3DAtomic isampler3D
/* Pass through functions. */
#define imageFence(image)
/* Backend Functions. */
#define select(A, B, mask) mix(A, B, mask)
/* Array syntax compatibility. */
#define float_array float[]
#define float2_array vec2[]
#define float3_array vec3[]
#define float4_array vec4[]
#define int_array int[]
#define int2_array int2[]
#define int3_array int3[]
#define int4_array int4[]
#define uint_array uint[]
#define uint2_array uint2[]
#define uint3_array uint3[]
#define uint4_array uint4[]
#define bool_array bool[]
#define bool2_array bool2[]
#define bool3_array bool3[]
#define bool4_array bool4[]
#define ARRAY_T(type) type[]
#define ARRAY_V
#define SHADER_LIBRARY_CREATE_INFO(a)
#define VERTEX_SHADER_CREATE_INFO(a)
#define FRAGMENT_SHADER_CREATE_INFO(a)
#define COMPUTE_SHADER_CREATE_INFO(a)
#define ATTR_FALLTHROUGH
#define _in_sta
#define _in_end
#define _out_sta
#define _out_end
#define _inout_sta
#define _inout_end
#define _shared_sta
#define _shared_end
/* References (inout and out).
* Less verbose that the above for reading processed code. */
#define _ref(_type, _var) inout _type _var
/* Constructor / initializer. */
#define _ctor(_type) _type(
#define _rotc() )
/* Resource accessor. */
#define specialization_constant_get(create_info, _res) _res
#define shared_variable_get(create_info, _res) _res
#define push_constant_get(create_info, _res) _res
#define interface_get(create_info, _res) _res
#define attribute_get(create_info, _res) _res
#define buffer_get(create_info, _res) _res
#define sampler_get(create_info, _res) _res
#define image_get(create_info, _res) _res
#define srt_access(create_info, _res) access_##create_info##_##_res()
/**
* WORKAROUND(fclem): Only used for cases when passing down the resource_table is impractical.
* Note that this placeholder is just for the code to compile.
*/
#define resource_table_get(table_type) table_type##_ctor_()
/* Incompatible keywords. */
#define static
#define constant
#define device
#define thread
#define threadgroup
/* MSL component compatibility. */
#define textureGather0(_tex, _co) textureGather(_tex, _co, 0)
#define textureGather1(_tex, _co) textureGather(_tex, _co, 1)
#define textureGather2(_tex, _co) textureGather(_tex, _co, 2)
#define textureGather3(_tex, _co) textureGather(_tex, _co, 3)
/**
* This string type is much like the OSL string.
* It is merely a hash of the actual string and it immutable.
* Named `string_t` to avoid name collision with `std::string`.
*/
struct string_t {
uint hash;
};
#if 0 /* Causes NVidia compiler error on OpenGL. To be fixed. */
bool equal(string_t a, string_t b)
{
return a.hash == b.hash;
}
#endif
uint as_uint(string_t str)
{
return str.hash;
}
float4 texelFetchExtend(sampler2D samp, int2 texel, int lvl)
{
texel = clamp(texel, int2(0), textureSize(samp, lvl).xy - 1);
return texelFetch(samp, texel, lvl);
}
/* For assert support. */
#if defined(GPU_VERTEX_SHADER)
# define GPU_THREAD uint3(gl_VertexID, gl_InstanceID, 0)
#elif defined(GPU_FRAGMENT_SHADER)
# define GPU_THREAD uint3(gl_FragCoord.x, gl_FragCoord.y, 0)
#elif defined(GPU_COMPUTE_SHADER)
# define GPU_THREAD gl_GlobalInvocationID
#else
# define GPU_THREAD error_not_in_a_shader_question_mark
#endif
/* Stage agnostic builtin function.
* GLSL doesn't allow mixing shader stages inside the same source file.
* Make sure builtin functions are stubbed when used in an invalid stage. */
#ifdef GPU_FRAGMENT_SHADER
# define gpu_discard_fragment() discard
# ifdef GPU_ARB_derivative_control
# define gpu_dfdx(x) dFdxFine(x)
# define gpu_dfdy(x) dFdyFine(x)
# else
# define gpu_dfdx(x) dFdx(x)
# define gpu_dfdy(x) dFdy(x)
# endif
# define gpu_fwidth(x) fwidth(x)
#else
# define gpu_discard_fragment()
# define gpu_dfdx(x) x
# define gpu_dfdy(x) x
# define gpu_fwidth(x) x
#endif

View File

@@ -0,0 +1,128 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Special header for mapping commonly defined tokens to API-specific variations.
* Where possible, this will adhere closely to base GLSL, where semantics are the same.
* However, host code shader code may need modifying to support types where necessary variations
* exist between APIs but are not expressed through the source. (e.g. distinction between depth2d
* and texture2d types in metal).
*/
#pragma once
#include "gpu_shader_msl_atomic.msl"
#include "gpu_shader_msl_attribute.msl"
#include "gpu_shader_msl_builtin.msl"
#include "gpu_shader_msl_image.msl"
#include "gpu_shader_msl_matrix.msl"
#include "gpu_shader_msl_matrix_legacy.msl"
#include "gpu_shader_msl_sampler.msl"
#include "gpu_shader_msl_string.msl"
#include "gpu_shader_msl_types.msl"
#include "gpu_shader_msl_types_legacy.msl"
/* Suppress unhelpful shader compiler warnings. */
#pragma clang diagnostic ignored "-Wunused-variable"
#pragma clang diagnostic ignored "-Wcomment"
/* Wrapper needs to be last. */
#include "gpu_shader_msl_wrapper.msl"
/* Array syntax compatibility. */
/* clang-format off */
#define float_array(...) { __VA_ARGS__ }
#define float2_array(...) { __VA_ARGS__ }
#define float3_array(...) { __VA_ARGS__ }
#define float4_array(...) { __VA_ARGS__ }
#define int_array(...) { __VA_ARGS__ }
#define int2_array(...) { __VA_ARGS__ }
#define int3_array(...) { __VA_ARGS__ }
#define int4_array(...) { __VA_ARGS__ }
#define uint_array(...) { __VA_ARGS__ }
#define uint2_array(...) { __VA_ARGS__ }
#define uint3_array(...) { __VA_ARGS__ }
#define uint4_array(...) { __VA_ARGS__ }
#define bool_array(...) { __VA_ARGS__ }
#define bool2_array(...) { __VA_ARGS__ }
#define bool3_array(...) { __VA_ARGS__ }
#define bool4_array(...) { __VA_ARGS__ }
#define ARRAY_T(type)
#define ARRAY_V(...) {__VA_ARGS__}
/* clang-format on */
#define SHADER_LIBRARY_CREATE_INFO(a)
#define VERTEX_SHADER_CREATE_INFO(a)
#define FRAGMENT_SHADER_CREATE_INFO(a)
#define COMPUTE_SHADER_CREATE_INFO(a)
#define ATTR_FALLTHROUGH
#define in
#define out thread
#define inout thread
#define _in_sta
#define _in_end
#define _out_sta (&
#define _out_end )
#define _inout_sta (&
#define _inout_end )
/* References (inout and out).
* Less verbose that the above for reading processed code. */
#define _ref(_type, _var) thread _type(&_var)
/* Constructor / initializer. */
#define _ctor(_type) \
_type \
{
#define _rotc() }
#define shared threadgroup
#define _shared_sta (&
#define _shared_end )
float4 texelFetchExtend(sampler2D samp, int2 texel, int lvl)
{
texel = clamp(texel, int2(0), textureSize(samp, lvl).xy - 1);
return texelFetch(samp, texel, lvl);
}
float4 texelFetchExtend(sampler2DDepth samp, int2 texel, int lvl)
{
texel = clamp(texel, int2(0), textureSize(samp, lvl).xy - 1);
return texelFetch(samp, texel, lvl);
}
/* Resource accessor. */
#define specialization_constant_get(create_info, _res) _res
#define shared_variable_get(create_info, _res) _res
#define push_constant_get(create_info, _res) _res
#define interface_get(create_info, _res) _res
#define attribute_get(create_info, _res) _res
#define buffer_get(create_info, _res) _res
#define sampler_get(create_info, _res) _res
#define image_get(create_info, _res) _res
#define srt_access(create_info, _res) access_##create_info##_##_res()
/**
* WORKAROUND(fclem): Only used for cases when passing down the resource_table is impractical.
* Note that this placeholder is just for the code to compile.
*/
#define resource_table_get(table_type) (table_type{})
/* For assert support. */
#if defined(GPU_VERTEX_SHADER)
# define GPU_THREAD uint3(gl_VertexID, gl_InstanceID, 0)
#elif defined(GPU_FRAGMENT_SHADER)
# define GPU_THREAD uint3(gl_FragCoord.x, gl_FragCoord.y, 0)
#elif defined(GPU_COMPUTE_SHADER)
# define GPU_THREAD gl_GlobalInvocationID
#else
# define GPU_THREAD error_not_in_a_shader_question_mark
#endif
/* Stage agnostic builtin function.
* MSL allow mixing shader stages inside the same source file.
* Leaving the calls untouched makes sure we catch invalid usage during CI testing. */
#define gpu_discard_fragment() discard
#define gpu_dfdx(x) dFdx(x)
#define gpu_dfdy(x) dFdy(x)
#define gpu_fwidth(x) fwidth(x)

View File

@@ -0,0 +1,155 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* BSL attributes.
*
* Define them as standard attribute with similar placement to trigger compiler warning about typos
* and error for misplacement.
*
* Actual implementation is done through the shader_tool and the attributes are not present in
* final shader code.
*/
#pragma once
#if !defined(_MSC_VER) || defined(__clang__)
/* MSVC doesn't support using the same attribute multiple times (fixed in later versions).
* However, Clang in MSVC compatibility mode does. */
/* Specify a function is a compute shader entry point. */
# define compute maybe_unused
/* Specify a function is a vertex shader entry point. */
# define vertex maybe_unused
/* Specify a function is a fragment shader entry point. */
# define fragment maybe_unused
/* Set compute shader workgroup size. */
# define local_size(...) maybe_unused
/* Request performing fragment tests before the fragment function executes. */
# define early_fragment_tests maybe_unused
/* Metal specific hints. To be used on entry point functions. */
# define metal_max_total_threads_per_threadgroup(count) maybe_unused
/* Enable shader patching on GL to remap clip range to 0..1. */
# define clip_control maybe_unused
/* Enable texture atomics on older versions of metal. */
# define texture_atomic maybe_unused
/* In a compute function, specify an input variable containing the 3-dimensional index of the local
* work invocation within the work group that the current shader is executing in. */
# define local_invocation_id maybe_unused
/* In a compute function, specify a derived input variable containing the 3-dimensional index of
* the work invocation within the global work group that the current shader is executing on. The
* value is equal to work_group_id * work_group_size + local_invocation_id. */
# define global_invocation_id maybe_unused
/* In a compute function, specify an 1-dimensional linearized index of the work invocation within
* the work group that the current shader is executing on. */
# define local_invocation_index maybe_unused
/* In a compute function, specify an input variable containing the 3-dimensional index of the
* global work group that the current compute shader invocation is executing within. */
# define work_group_id maybe_unused
/* In a compute function, specify an input variable containing the total number of work groups that
* will execute for the current compute shader dispatch. */
# define num_work_groups maybe_unused
/* Specify a vertex attribute. */
# define attribute(slot) maybe_unused
/* Vertex attribute interpolation modes. */
# define flat maybe_unused
# define smooth maybe_unused
# define no_perspective maybe_unused
/* Vertex shader output position. */
# define position maybe_unused
/* Vertex shader output point size. */
# define point_size maybe_unused
/* Vertex shader output, distance from vertex to clipping plane. */
# define clip_distance maybe_unused
/* The render target array index. */
# define layer maybe_unused
/* The viewport (and scissor rectangle) index value of the primitive. */
# define viewport_index maybe_unused
/* Vertex shader input vertex index, which includes the base vertex if one is specified. */
# define vertex_id maybe_unused
/* Vertex shader input instance index, which doesn't include the base instance. */
# define instance_id maybe_unused
/* Vertex shader input instance index, which includes the base instance if one is specified. */
# define instance_index maybe_unused
/* Vertex shader input base instance value added to each instance identifier before reading
* per-instance data. */
# define base_instance maybe_unused
# define frag_coord maybe_unused
# define point_coord maybe_unused
# define front_facing maybe_unused
/* Fragment shader color input index for subpass input.
* `sampler_type` is the type of image to bind to this (e.g. usampler2DArray).
* It must be compatible with the frame-buffer attachment type. */
# define subpass_input(index, sampler_type) maybe_unused
/* Fragment shader output. */
# define frag_color(slot) maybe_unused
/* Fragment shader output. */
# define frag_depth(mode) maybe_unused
/* Fragment shader output. Set stencil reference value per pixel.
* Only supported on some platform. Check for compatibility first. */
# define frag_stencil_ref maybe_unused
/* Fragment shader color output index for dual source blending. */
# define index(i) maybe_unused
/* Fragment shader color output index for raster order group on Metal. */
# define raster_order_group(i) maybe_unused
/* Graphic pipeline stage in/out. */
# define in maybe_unused
# define out maybe_unused
# define subpass_in maybe_unused
/* Declare a dependency to a legacy create info whose name is the struct member name. */
# define legacy_info maybe_unused
/* Declare a sampler at the given slot. */
# define sampler(slot) maybe_unused
/* Declare a uniform buffer at the given slot. */
# define uniform(slot) maybe_unused
/* Declare a storage buffer at the given slot. */
# define storage(slot, qualifiers) maybe_unused
/* Declare a storage buffer at the given slot. */
# define image(slot, qualifiers, format) maybe_unused
# define compilation_constant maybe_unused
# define specialization_constant(default_value) maybe_unused
# define push_constant maybe_unused
/* Declare a nested resource table member. */
# define resource_table maybe_unused
/* Only declare the member if cond evaluates to true. */
# define condition(cond) maybe_unused
/* Set binding frequency of a resource (storage, uniform, image, sampler). */
# define frequency(freq) maybe_unused
/* Make a structure layout or enum shared between CPU and GPU code.
* Required for structs defining storage and uniform buffer layout. */
# define host_shared
/** Make function callable thought the node-tree code-generating system. */
# define node maybe_unused
/* Make the branch condition evaluate at compile time. */
# define static_branch likely
/* Unroll the loop at compile time. */
# define unroll likely
/**
* Unroll the loop N time at compile time.
* IMPORTANT: Will discard any iteration above N.
*/
# define unroll_n(N) likely
#else
/* This path checks for unused variables. Disable warning about unknown attributes. */
# if defined(__GNUC__) || defined(__clang__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wattributes"
# elif defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable : 5030)
# pragma warning(disable : 5222)
# endif
#endif

View File

@@ -0,0 +1,233 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* C++ stubs for shading language.
*
* IMPORTANT: Please ask the module team if you need some feature that are not listed in this file.
*/
#pragma once
#include "gpu_shader_cxx_vector.hh"
/* Some compilers complain about lack of return values. Keep it short. */
#define RET \
{ \
return {}; \
}
/* -------------------------------------------------------------------- */
/** \name Builtin Functions
* \{ */
template<typename T, int D> VecBase<bool, D> greaterThan(VecOp<T, D>, VecOp<T, D>) RET;
template<typename T, int D> VecBase<bool, D> lessThan(VecOp<T, D>, VecOp<T, D>) RET;
template<typename T, int D> VecBase<bool, D> lessThanEqual(VecOp<T, D>, VecOp<T, D>) RET;
template<typename T, int D> VecBase<bool, D> greaterThanEqual(VecOp<T, D>, VecOp<T, D>) RET;
template<typename T, int D> VecBase<bool, D> equal(VecOp<T, D>, VecOp<T, D>) RET;
template<typename T, int D> VecBase<bool, D> notEqual(VecOp<T, D>, VecOp<T, D>) RET;
template<int D> bool any(VecOp<bool, D>) RET;
template<int D> bool all(VecOp<bool, D>) RET;
/* `not` is a C++ keyword that aliases the `!` operator. Simply overload it. */
template<int D> VecBase<bool, D> operator!(VecOp<bool, D>) RET;
template<int D> VecBase<int, D> bitCount(VecOp<int, D>) RET;
template<int D> VecBase<int, D> bitCount(VecOp<uint, D>) RET;
template<int D> VecBase<int, D> bitfieldExtract(VecOp<int, D>, int, int) RET;
template<int D> VecBase<uint, D> bitfieldExtract(VecOp<uint, D>, int, int) RET;
template<int D> VecBase<int, D> bitfieldInsert(VecOp<int, D>, VecOp<int, D>, int, int) RET;
template<int D> VecBase<uint, D> bitfieldInsert(VecOp<uint, D>, VecOp<uint, D>, int, int) RET;
template<int D> VecBase<int, D> bitfieldReverse(VecOp<int, D>) RET;
template<int D> VecBase<uint, D> bitfieldReverse(VecOp<uint, D>) RET;
int bitCount(int) RET;
int bitCount(uint) RET;
int bitfieldExtract(int) RET;
uint bitfieldExtract(uint) RET;
int bitfieldInsert(int) RET;
uint bitfieldInsert(uint) RET;
int bitfieldReverse(int) RET;
uint bitfieldReverse(uint) RET;
template<int D> VecBase<int, D> findLSB(VecOp<int, D>) RET;
template<int D> VecBase<int, D> findLSB(VecOp<uint, D>) RET;
template<int D> VecBase<int, D> findMSB(VecOp<int, D>) RET;
template<int D> VecBase<int, D> findMSB(VecOp<uint, D>) RET;
int findLSB(int) RET;
int findLSB(uint) RET;
int findMSB(int) RET;
int findMSB(uint) RET;
/* Math Functions. */
/* NOTE: Declared inside a namespace and exposed behind macros to prevent
* errors on VS2019 due to `corecrt_math` conflicting functions. */
namespace glsl {
template<typename T> constexpr T abs(T) RET;
/* TODO(fclem): These should be restricted to floats. */
template<typename T> constexpr T ceil(T) RET;
template<typename T> constexpr T exp(T) RET;
template<typename T> constexpr T exp2(T) RET;
template<typename T> constexpr T floor(T) RET;
template<typename T> T fma(T, T, T) RET;
float fma(float, float, float) RET;
template<typename T> T frexp(T, T) RET;
bool isinf(float) RET;
template<int D> VecBase<bool, D> isinf(VecOp<float, D>) RET;
bool isnan(float) RET;
template<int D> VecBase<bool, D> isnan(VecOp<float, D>) RET;
template<typename T> constexpr T log(T) RET;
template<typename T> constexpr T log2(T) RET;
template<typename T> T modf(T, T &) RET;
template<typename T, typename U> constexpr T pow(T, U) RET;
template<typename T> constexpr T round(T) RET;
template<typename T> constexpr T sqrt(T) RET;
template<typename T> constexpr T trunc(T) RET;
template<typename T, typename U> T ldexp(T, U) RET;
template<typename T> constexpr T acos(T) RET;
template<typename T> T acosh(T) RET;
template<typename T> constexpr T asin(T) RET;
template<typename T> T asinh(T) RET;
template<typename T> T atan(T, T) RET;
template<typename T> T atan(T) RET;
template<typename T> T atanh(T) RET;
template<typename T> constexpr T cos(T) RET;
template<typename T> T cosh(T) RET;
template<typename T> constexpr T sin(T) RET;
template<typename T> T sinh(T) RET;
template<typename T> T tan(T) RET;
template<typename T> T tanh(T) RET;
} // namespace glsl
#define abs glsl::abs
#define ceil glsl::ceil
#define exp glsl::exp
#define exp2 glsl::exp2
#define floor glsl::floor
#define fma glsl::fma
#define frexp glsl::frexp
#define isinf glsl::isinf
#define isnan glsl::isnan
#define log glsl::log
#define log2 glsl::log2
#define modf glsl::modf
#define pow glsl::pow
#define round glsl::round
#define sqrt glsl::sqrt
#define trunc glsl::trunc
#define ldexp glsl::ldexp
#define acos glsl::acos
#define acosh glsl::acosh
#define asin glsl::asin
#define asinh glsl::asinh
#define atan glsl::atan
#define atanh glsl::atanh
#define cos glsl::cos
#define cosh glsl::cosh
#define sin glsl::sin
#define sinh glsl::sinh
#define tan glsl::tan
#define tanh glsl::tanh
template<typename T> constexpr T max(T, T) RET;
template<typename T> constexpr T min(T, T) RET;
template<typename T> constexpr T sign(T) RET;
template<typename T, typename U> constexpr T clamp(T, U, U) RET;
template<typename T> constexpr T clamp(T, float, float) RET;
template<typename T, typename U> constexpr T max(T, U) RET;
template<typename T, typename U> constexpr T min(T, U) RET;
/* TODO(fclem): These should be restricted to floats. */
template<typename T> T fract(T) RET;
template<typename T> constexpr T inversesqrt(T) RET;
constexpr float mod(float, float) RET;
template<int D> VecBase<float, D> constexpr mod(VecOp<float, D>, float) RET;
template<int D> VecBase<float, D> constexpr mod(VecOp<float, D>, VecOp<float, D>) RET;
template<typename T> T smoothstep(T, T, T) RET;
float step(float, float) RET;
template<int D> VecBase<float, D> step(VecOp<float, D>, VecOp<float, D>) RET;
template<int D> VecBase<float, D> step(float, VecOp<float, D>) RET;
float smoothstep(float, float, float) RET;
template<int D> VecBase<float, D> smoothstep(float, float, VecOp<float, D>) RET;
template<typename T> constexpr T degrees(T) RET;
template<typename T> constexpr T radians(T) RET;
/* Declared explicitly to avoid type errors. */
float mix(float, float, float) RET;
template<int D> VecBase<float, D> mix(VecOp<float, D>, VecOp<float, D>, float) RET;
template<int D> VecBase<float, D> mix(VecOp<float, D>, VecOp<float, D>, VecOp<float, D>) RET;
template<typename T, int D> VecBase<T, D> mix(VecOp<T, D>, VecOp<T, D>, VecOp<bool, D>) RET;
#define select(A, B, C) mix(A, B, C)
VecBase<float, 3> cross(VecOp<float, 3>, VecOp<float, 3>) RET;
template<int D> float dot(VecOp<float, D>, VecOp<float, D>) RET;
float distance(float, float) RET;
template<int D> float distance(VecOp<float, D>, VecOp<float, D>) RET;
template<int D> float length(VecOp<float, D>) RET;
template<int D> VecBase<float, D> normalize(VecOp<float, D>) RET;
template<int D> VecBase<int, D> floatBitsToInt(VecOp<float, D>) RET;
template<int D> VecBase<uint, D> floatBitsToUint(VecOp<float, D>) RET;
template<int D> VecBase<float, D> intBitsToFloat(VecOp<int, D>) RET;
template<int D> VecBase<float, D> uintBitsToFloat(VecOp<uint, D>) RET;
int floatBitsToInt(float) RET;
uint floatBitsToUint(float) RET;
float intBitsToFloat(int) RET;
float uintBitsToFloat(uint) RET;
/* Derivative functions. */
template<typename T> T gpu_dfdx(T) RET;
template<typename T> T gpu_dfdy(T) RET;
template<typename T> T gpu_fwidth(T) RET;
/* Discards the output of the current fragment shader invocation and halts its execution. */
void gpu_discard_fragment() {}
/* Geometric functions. */
template<typename T, int D> VecBase<T, D> faceforward(VecOp<T, D>, VecOp<T, D>, VecOp<T, D>) RET;
template<typename T, int D> VecBase<T, D> reflect(VecOp<T, D>, VecOp<T, D>) RET;
template<typename T, int D> VecBase<T, D> refract(VecOp<T, D>, VecOp<T, D>, float) RET;
/* Atomic operations. */
int atomicAdd(int &, int) RET;
int atomicAnd(int &, int) RET;
int atomicOr(int &, int) RET;
int atomicXor(int &, int) RET;
int atomicMin(int &, int) RET;
int atomicMax(int &, int) RET;
int atomicExchange(int &, int) RET;
int atomicCompSwap(int &, int, int) RET;
uint atomicAdd(uint &, uint) RET;
uint atomicAnd(uint &, uint) RET;
uint atomicOr(uint &, uint) RET;
uint atomicXor(uint &, uint) RET;
uint atomicMin(uint &, uint) RET;
uint atomicMax(uint &, uint) RET;
uint atomicExchange(uint &, uint) RET;
uint atomicCompSwap(uint &, uint, uint) RET;
/* Packing functions. */
uint packHalf2x16(float2) RET;
uint packUnorm2x16(float2) RET;
uint packSnorm2x16(float2) RET;
uint packUnorm4x8(float4) RET;
uint packSnorm4x8(float4) RET;
float2 unpackHalf2x16(uint) RET;
float2 unpackUnorm2x16(uint) RET;
float2 unpackSnorm2x16(uint) RET;
float4 unpackUnorm4x8(uint) RET;
float4 unpackSnorm4x8(uint) RET;
void barrier() {}
void memoryBarrier() {}
void memoryBarrierShared() {}
void memoryBarrierImage() {}
void memoryBarrierBuffer() {}
void groupMemoryBarrier() {}
/** \} */
#undef RET

View File

@@ -0,0 +1,60 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* C++ stubs for shading language.
*
* IMPORTANT: Please ask the module team if you need some feature that are not listed in this file.
*/
#pragma once
#include "gpu_shader_cxx_vector.hh"
/* -------------------------------------------------------------------- */
/** \name Special Variables
* \{ */
namespace gl_VertexShader {
extern const int gl_VertexID;
extern const int gl_InstanceID;
extern const int gl_BaseVertex;
extern const int gpu_BaseInstance;
extern const int gpu_InstanceIndex;
float4 gl_Position = float4(0);
float gl_PointSize = 0;
float gl_ClipDistance[6] = {0};
int gpu_Layer = 0;
int gpu_ViewportIndex = 0;
} // namespace gl_VertexShader
namespace gl_FragmentShader {
extern const float4 gl_FragCoord;
const bool gl_FrontFacing = true;
const float2 gl_PointCoord = float2(0);
const int gl_PrimitiveID = 0;
float gl_FragDepth = 0;
const float gl_ClipDistance[6] = {0};
const int gpu_Layer = 0;
const int gpu_ViewportIndex = 0;
} // namespace gl_FragmentShader
/* Outside of namespace to be used in create infos. */
constexpr uint3 gl_WorkGroupSize = uint3(16, 16, 16);
namespace gl_ComputeShader {
extern const uint3 gl_NumWorkGroups;
extern const uint3 gl_WorkGroupID;
extern const uint3 gl_LocalInvocationID;
extern const uint3 gl_GlobalInvocationID;
extern const uint gl_LocalInvocationIndex;
} // namespace gl_ComputeShader
/** \} */

View File

@@ -0,0 +1,115 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* C++ stubs for shading language.
*
* IMPORTANT: Please ask the module team if you need some feature that are not listed in this file.
*/
#pragma once
#include "gpu_shader_cxx_vector.hh"
/* Some compilers complain about lack of return values. Keep it short. */
#define RET \
{ \
return {}; \
}
/* -------------------------------------------------------------------- */
/** \name Image Types
* \{ */
template<typename T, int Dimensions, bool Array = false, bool Atomic = false> struct ImageBase {
static constexpr int coord_dim = Dimensions + int(Array);
using int_coord_type = VecBase<int, coord_dim>;
using data_vec_type = VecBase<T, 4>;
using size_vec_type = VecBase<int, coord_dim>;
};
#define IMG_TEMPLATE \
template<typename T, \
typename IntCoord = typename T::int_coord_type, \
typename DataVec = typename T::data_vec_type, \
typename SizeVec = typename T::size_vec_type>
IMG_TEMPLATE SizeVec imageSize(const T &) RET;
IMG_TEMPLATE DataVec imageLoad(const T &, IntCoord) RET;
IMG_TEMPLATE void imageStore(T &, IntCoord, DataVec) {}
IMG_TEMPLATE void imageFence(T &) {}
/* Cannot write to a read only image. */
IMG_TEMPLATE void imageStore(const T &, IntCoord, DataVec) = delete;
IMG_TEMPLATE void imageFence(const T &) = delete;
#define imageLoadFast imageLoad
#define imageStoreFast imageStore
IMG_TEMPLATE uint imageAtomicAdd(T &, IntCoord, uint) RET;
IMG_TEMPLATE uint imageAtomicMin(T &, IntCoord, uint) RET;
IMG_TEMPLATE uint imageAtomicMax(T &, IntCoord, uint) RET;
IMG_TEMPLATE uint imageAtomicAnd(T &, IntCoord, uint) RET;
IMG_TEMPLATE uint imageAtomicXor(T &, IntCoord, uint) RET;
IMG_TEMPLATE uint imageAtomicOr(T &, IntCoord, uint) RET;
IMG_TEMPLATE uint imageAtomicExchange(T &, IntCoord, uint) RET;
IMG_TEMPLATE uint imageAtomicCompSwap(T &, IntCoord, uint, uint) RET;
IMG_TEMPLATE int imageAtomicAdd(T &, IntCoord, int) RET;
IMG_TEMPLATE int imageAtomicMin(T &, IntCoord, int) RET;
IMG_TEMPLATE int imageAtomicMax(T &, IntCoord, int) RET;
IMG_TEMPLATE int imageAtomicAnd(T &, IntCoord, int) RET;
IMG_TEMPLATE int imageAtomicXor(T &, IntCoord, int) RET;
IMG_TEMPLATE int imageAtomicOr(T &, IntCoord, int) RET;
IMG_TEMPLATE int imageAtomicExchange(T &, IntCoord, int) RET;
IMG_TEMPLATE int imageAtomicCompSwap(T &, IntCoord, int, int) RET;
/* Cannot write to a read only image. */
IMG_TEMPLATE uint imageAtomicAdd(const T &, IntCoord, uint) = delete;
IMG_TEMPLATE uint imageAtomicMin(const T &, IntCoord, uint) = delete;
IMG_TEMPLATE uint imageAtomicMax(const T &, IntCoord, uint) = delete;
IMG_TEMPLATE uint imageAtomicAnd(const T &, IntCoord, uint) = delete;
IMG_TEMPLATE uint imageAtomicXor(const T &, IntCoord, uint) = delete;
IMG_TEMPLATE uint imageAtomicOr(const T &, IntCoord, uint) = delete;
IMG_TEMPLATE uint imageAtomicExchange(const T &, IntCoord, uint) = delete;
IMG_TEMPLATE uint imageAtomicCompSwap(const T &, IntCoord, uint, uint) = delete;
IMG_TEMPLATE int imageAtomicAdd(const T &, IntCoord, int) = delete;
IMG_TEMPLATE int imageAtomicMin(const T &, IntCoord, int) = delete;
IMG_TEMPLATE int imageAtomicMax(const T &, IntCoord, int) = delete;
IMG_TEMPLATE int imageAtomicAnd(const T &, IntCoord, int) = delete;
IMG_TEMPLATE int imageAtomicXor(const T &, IntCoord, int) = delete;
IMG_TEMPLATE int imageAtomicOr(const T &, IntCoord, int) = delete;
IMG_TEMPLATE int imageAtomicExchange(const T &, IntCoord, int) = delete;
IMG_TEMPLATE int imageAtomicCompSwap(const T &, IntCoord, int, int) = delete;
#undef IMG_TEMPLATE
using image1D = ImageBase<float, 1>;
using image2D = ImageBase<float, 2>;
using image3D = ImageBase<float, 3>;
using iimage1D = ImageBase<int, 1>;
using iimage2D = ImageBase<int, 2>;
using iimage3D = ImageBase<int, 3>;
using uimage1D = ImageBase<uint, 1>;
using uimage2D = ImageBase<uint, 2>;
using uimage3D = ImageBase<uint, 3>;
using image1DArray = ImageBase<float, 1, true>;
using image2DArray = ImageBase<float, 2, true>;
using iimage1DArray = ImageBase<int, 1, true>;
using iimage2DArray = ImageBase<int, 2, true>;
using uimage1DArray = ImageBase<uint, 1, true>;
using uimage2DArray = ImageBase<uint, 2, true>;
using iimage2DAtomic = ImageBase<int, 2, false, true>;
using iimage3DAtomic = ImageBase<int, 3, false, true>;
using uimage2DAtomic = ImageBase<uint, 2, false, true>;
using uimage3DAtomic = ImageBase<uint, 3, false, true>;
using iimage2DArrayAtomic = ImageBase<int, 2, true, true>;
using uimage2DArrayAtomic = ImageBase<uint, 2, true, true>;
/* Forbid Cube and cube arrays. Bind them as 3D textures instead. */
/** \} */
#undef RET

View File

@@ -0,0 +1,154 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* C++ stubs for shading language.
*
* IMPORTANT: Please ask the module team if you need some feature that are not listed in this file.
*/
#pragma once
#include "gpu_shader_cxx_builtin.hh"
#include "gpu_shader_cxx_vector.hh"
/* Some compilers complain about lack of return values. Keep it short. */
#define RET \
{ \
return {}; \
}
/* -------------------------------------------------------------------- */
/** \name Matrix Types
* \{ */
template<int C, int R> struct MatBase {};
template<int C, int R> struct MatOp {
using MatT = MatBase<C, R>;
using ColT = VecBase<float, R>;
using RowT = VecBase<float, C>;
const ColT &operator[](int) const
{
return *reinterpret_cast<const ColT *>(this);
}
ColT &operator[](int)
{
return *reinterpret_cast<ColT *>(this);
}
const ColT &operator[](uint) const
{
return *reinterpret_cast<const ColT *>(this);
}
ColT &operator[](uint)
{
return *reinterpret_cast<ColT *>(this);
}
MatT operator+() RET;
MatT operator-() RET;
MatT operator*(MatT) const RET;
friend RowT operator*(ColT, MatT) RET;
friend ColT operator*(MatT, RowT) RET;
};
template<int R> struct MatBase<2, R> : MatOp<2, R> {
using T = float;
using ColT = VecBase<float, R>;
ColT x, y;
MatBase() = default;
explicit MatBase(T) {}
explicit MatBase(T, T, T, T) {}
explicit MatBase(ColT, ColT) {}
template<int OtherC, int OtherR> explicit MatBase(const MatBase<OtherC, OtherR> &) {}
};
template<int R> struct MatBase<3, R> : MatOp<3, R> {
using T = float;
using ColT = VecBase<float, R>;
ColT x, y, z;
MatBase() = default;
explicit MatBase(T) {}
explicit MatBase(T, T, T, T, T, T, T, T, T) {}
explicit MatBase(ColT, ColT, ColT) {}
template<int OtherC, int OtherR> explicit MatBase(const MatBase<OtherC, OtherR> &) {}
};
template<int R> struct MatBase<4, R> : MatOp<4, R> {
using T = float;
using ColT = VecBase<float, R>;
ColT x, y, z, w;
MatBase() = default;
explicit MatBase(T) {}
explicit MatBase(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T) {}
explicit MatBase(ColT, ColT, ColT, ColT) {}
template<int OtherC, int OtherR> explicit MatBase(const MatBase<OtherC, OtherR> &) {}
};
using float2x2 = MatBase<2, 2>;
using float2x3 = MatBase<2, 3>;
using float2x4 = MatBase<2, 4>;
using float3x2 = MatBase<3, 2>;
using float3x3 = MatBase<3, 3>;
using float3x4 = MatBase<3, 4>;
using float4x2 = MatBase<4, 2>;
using float4x3 = MatBase<4, 3>;
using float4x4 = MatBase<4, 4>;
/* Matrix reshaping functions. */
#define RESHAPE(mat_to, mat_from, ...) \
mat_to to_##mat_to(mat_from m) \
{ \
return mat_to(__VA_ARGS__); \
}
/* clang-format off */
RESHAPE(float2x2, float3x3, m[0].xy, m[1].xy)
RESHAPE(float2x2, float4x4, m[0].xy, m[1].xy)
RESHAPE(float3x3, float4x4, m[0].xyz, m[1].xyz, m[2].xyz)
RESHAPE(float3x3, float2x2, m[0].x, m[0].y, 0, m[1].x, m[1].y, 0, 0, 0, 1)
RESHAPE(float4x4, float2x2, m[0].x, m[0].y, 0, 0, m[1].x, m[1].y, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
RESHAPE(float4x4, float3x3, m[0].x, m[0].y, m[0].z, 0, m[1].x, m[1].y, m[1].z, 0, m[2].x, m[2].y, m[2].z, 0, 0, 0, 0, 1)
/* clang-format on */
/* TODO(fclem): Remove. Use Transform instead. */
RESHAPE(float3x3, float3x4, m[0].xyz, m[1].xyz, m[2].xyz)
#undef RESHAPE
/* Matrix compare operators. */
#define EQ_OP(type, ...) \
inline bool operator==(type a, type b) \
{ \
return __VA_ARGS__; \
}
EQ_OP(float2x2, all(equal(a[0], b[0])) && all(equal(a[1], b[1])))
EQ_OP(float2x3, all(equal(a[0], b[0])) && all(equal(a[1], b[1])))
EQ_OP(float2x4, all(equal(a[0], b[0])) && all(equal(a[1], b[1])))
EQ_OP(float3x2, all(equal(a[0], b[0])) && all(equal(a[1], b[1])) && all(equal(a[2], b[2])))
EQ_OP(float3x3, all(equal(a[0], b[0])) && all(equal(a[1], b[1])) && all(equal(a[2], b[2])))
EQ_OP(float3x4, all(equal(a[0], b[0])) && all(equal(a[1], b[1])) && all(equal(a[2], b[2])))
EQ_OP(float4x2,
all(equal(a[0], b[0])) && all(equal(a[1], b[1])) && all(equal(a[2], b[2])) &&
all(equal(a[3], b[3])))
EQ_OP(float4x3,
all(equal(a[0], b[0])) && all(equal(a[1], b[1])) && all(equal(a[2], b[2])) &&
all(equal(a[3], b[3])))
EQ_OP(float4x4,
all(equal(a[0], b[0])) && all(equal(a[1], b[1])) && all(equal(a[2], b[2])) &&
all(equal(a[3], b[3])))
#undef EQ_OP
/* Matrices functions. */
template<int C, int R> float determinant(MatBase<C, R>) RET;
template<int C, int R> MatBase<C, R> inverse(MatBase<C, R>) RET;
template<int C, int R> MatBase<R, C> transpose(MatBase<C, R>) RET;
/** \} */
#undef RET

View File

@@ -0,0 +1,115 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* C++ stubs for shading language.
*
* IMPORTANT: Please ask the module team if you need some feature that are not listed in this file.
*/
#pragma once
#include "gpu_shader_cxx_vector.hh"
/* Some compilers complain about lack of return values. Keep it short. */
#define RET \
{ \
return {}; \
}
/* -------------------------------------------------------------------- */
/** \name Sampler Types
* \{ */
template<typename T,
int Dimensions,
bool Cube = false,
bool Array = false,
bool Atomic = false,
bool Depth = false>
struct SamplerBase {
static constexpr int coord_dim = Dimensions + int(Cube) + int(Array);
static constexpr int deriv_dim = Dimensions + int(Cube);
static constexpr int extent_dim = Dimensions + int(Array);
using int_coord_type = VecBase<int, coord_dim>;
using flt_coord_type = VecBase<float, coord_dim>;
using derivative_type = VecBase<float, deriv_dim>;
using data_vec_type = VecBase<T, 4>;
using size_vec_type = VecBase<int, extent_dim>;
};
#define TEX_TEMPLATE \
template<typename T, \
typename IntCoord = typename T::int_coord_type, \
typename FltCoord = typename T::flt_coord_type, \
typename DerivVec = typename T::derivative_type, \
typename DataVec = typename T::data_vec_type, \
typename SizeVec = typename T::size_vec_type>
TEX_TEMPLATE SizeVec textureSize(T, int) RET;
TEX_TEMPLATE DataVec texelFetch(T, IntCoord, int) RET;
TEX_TEMPLATE DataVec texelFetchOffset(T, IntCoord, int, IntCoord) RET;
TEX_TEMPLATE DataVec texture(T, FltCoord, float /*bias*/ = 0.0f) RET;
TEX_TEMPLATE DataVec textureGather(T, FltCoord, int /*comp*/ = 0) RET;
TEX_TEMPLATE DataVec textureGrad(T, FltCoord, DerivVec, DerivVec) RET;
TEX_TEMPLATE DataVec textureLod(T, FltCoord, float) RET;
TEX_TEMPLATE DataVec textureLodOffset(T, FltCoord, float, IntCoord) RET;
#undef TEX_TEMPLATE
using samplerBuffer = SamplerBase<float, 1>;
using sampler1D = SamplerBase<float, 1>;
using sampler2D = SamplerBase<float, 2>;
using sampler3D = SamplerBase<float, 3>;
using isamplerBuffer = SamplerBase<int, 1>;
using isampler1D = SamplerBase<int, 1>;
using isampler2D = SamplerBase<int, 2>;
using isampler3D = SamplerBase<int, 3>;
using usamplerBuffer = SamplerBase<uint, 1>;
using usampler1D = SamplerBase<uint, 1>;
using usampler2D = SamplerBase<uint, 2>;
using usampler3D = SamplerBase<uint, 3>;
using sampler1DArray = SamplerBase<float, 1, false, true>;
using sampler2DArray = SamplerBase<float, 2, false, true>;
using isampler1DArray = SamplerBase<int, 1, false, true>;
using isampler2DArray = SamplerBase<int, 2, false, true>;
using usampler1DArray = SamplerBase<uint, 1, false, true>;
using usampler2DArray = SamplerBase<uint, 2, false, true>;
using samplerCube = SamplerBase<float, 2, true>;
using isamplerCube = SamplerBase<int, 2, true>;
using usamplerCube = SamplerBase<uint, 2, true>;
using samplerCubeArray = SamplerBase<float, 2, true, true>;
using isamplerCubeArray = SamplerBase<int, 2, true, true>;
using usamplerCubeArray = SamplerBase<uint, 2, true, true>;
using usampler1DAtomic = SamplerBase<uint, 1, false, false, true>;
using usampler2DAtomic = SamplerBase<uint, 2, false, false, true>;
using usampler2DArrayAtomic = SamplerBase<uint, 2, false, true, true>;
using usampler3DAtomic = SamplerBase<uint, 3, false, false, true>;
using isampler1DAtomic = SamplerBase<int, 1, false, false, true>;
using isampler2DAtomic = SamplerBase<int, 2, false, false, true>;
using isampler2DArrayAtomic = SamplerBase<int, 2, false, true, true>;
using isampler3DAtomic = SamplerBase<int, 3, false, false, true>;
using sampler2DDepth = SamplerBase<float, 2, false, false, false, true>;
using sampler2DArrayDepth = SamplerBase<float, 2, false, true, false, true>;
using samplerCubeDepth = SamplerBase<float, 2, true, false, false, true>;
using samplerCubeArrayDepth = SamplerBase<float, 2, true, true, false, true>;
/* Sampler Buffers do not have LOD. */
float4 texelFetch(samplerBuffer, int) RET;
int4 texelFetch(isamplerBuffer, int) RET;
uint4 texelFetch(usamplerBuffer, int) RET;
float4 texelFetchExtend(sampler2D /*samp*/, int2 /*texel*/, int /*lvl*/) RET;
float4 texelFetchExtend(sampler2DDepth /*samp*/, int2 /*texel*/, int /*lvl*/) RET;
/** \} */
#undef RET

View File

@@ -0,0 +1,34 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* C++ stubs for shading language.
*
* IMPORTANT: Please ask the module team if you need some feature that are not listed in this file.
*/
#pragma once
/* For uint declaration. */
#include "gpu_shader_cxx_vector.hh" // IWYU pragma: export
/**
* Placeholder type for the actual shading language type.
* This string type is much like the OSL string.
* It is merely a hash of the actual string and it immutable.
* Named `string_t` to avoid name collision with `std::string`.
*/
struct string_t {
string_t(const char * /*str*/) {}
};
bool equal(string_t, string_t)
{
return false;
}
uint as_uint(string_t)
{
return 1;
}

View File

@@ -0,0 +1,446 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* C++ stubs for shading language.
*
* IMPORTANT: Please ask the module team if you need some feature that are not listed in this file.
*/
#pragma once
/* Implement type_trait manually to avoid dragging compile time down. */
namespace cxx {
template<typename T, typename U> struct is_same {
static constexpr bool value = false;
};
template<typename T> struct is_same<T, T> {
static constexpr bool value = true;
};
template<typename T, typename U> inline constexpr bool is_same_v = is_same<T, U>::value;
template<typename T> struct is_integral_base {
static constexpr bool value = false;
};
// Helper macro to stamp out the true cases quickly
#define REGISTER_INTEGRAL(TYPE) \
template<> struct is_integral_base<TYPE> { \
static constexpr bool value = true; \
};
REGISTER_INTEGRAL(char)
REGISTER_INTEGRAL(signed char)
REGISTER_INTEGRAL(unsigned char)
REGISTER_INTEGRAL(short)
REGISTER_INTEGRAL(unsigned short)
REGISTER_INTEGRAL(int)
REGISTER_INTEGRAL(unsigned int)
REGISTER_INTEGRAL(long)
REGISTER_INTEGRAL(unsigned long)
REGISTER_INTEGRAL(long long)
REGISTER_INTEGRAL(unsigned long long)
template<typename T> inline constexpr bool is_integral_v = is_integral_base<T>::value;
} // namespace cxx
#undef REGISTER_INTEGRAL
/* Some compilers complain about lack of return values. Keep it short. */
#define RET \
{ \
return {}; \
}
template<typename T>
concept NotBool = !cxx::is_same_v<T, bool>;
template<typename T>
concept IsIntegral = cxx::is_integral_v<T> && !cxx::is_same_v<T, bool>;
/* -------------------------------------------------------------------- */
/** \name Vector Types
* \{ */
template<typename T, int Sz> struct VecBase {};
template<typename T, int Sz> struct VecOp {
using VecT = VecBase<T, Sz>;
const T &operator[](int) const
{
return *reinterpret_cast<const T *>(this);
}
const T &operator[](unsigned int) const
{
return *reinterpret_cast<const T *>(this);
}
T &operator[](int)
{
return *reinterpret_cast<T *>(this);
}
T &operator[](unsigned int)
{
return *reinterpret_cast<T *>(this);
}
#define STD_OP requires NotBool<T>
VecT operator+() const STD_OP RET;
VecT operator-() const STD_OP RET;
friend VecT operator+(VecT, VecT) STD_OP RET;
friend VecT operator-(VecT, VecT) STD_OP RET;
friend VecT operator/(VecT, VecT) STD_OP RET;
friend VecT operator*(VecT, VecT) STD_OP RET;
friend VecT operator+(VecT, T) STD_OP RET;
friend VecT operator-(VecT, T) STD_OP RET;
friend VecT operator/(VecT, T) STD_OP RET;
friend VecT operator*(VecT, T) STD_OP RET;
friend VecT operator+(T, VecT) STD_OP RET;
friend VecT operator-(T, VecT) STD_OP RET;
friend VecT operator/(T, VecT) STD_OP RET;
friend VecT operator*(T, VecT) STD_OP RET;
friend VecT operator+=(VecT, VecT) STD_OP RET;
friend VecT operator-=(VecT, VecT) STD_OP RET;
friend VecT operator/=(VecT, VecT) STD_OP RET;
friend VecT operator*=(VecT, VecT) STD_OP RET;
friend VecT operator+=(VecT, T) STD_OP RET;
friend VecT operator-=(VecT, T) STD_OP RET;
friend VecT operator/=(VecT, T) STD_OP RET;
friend VecT operator*=(VecT, T) STD_OP RET;
#undef STD_OP
#define INT_OP requires IsIntegral<T>
friend VecT operator~(VecT) INT_OP RET;
friend VecT operator%(VecT, VecT) INT_OP RET;
friend VecT operator&(VecT, VecT) INT_OP RET;
friend VecT operator|(VecT, VecT) INT_OP RET;
friend VecT operator^(VecT, VecT) INT_OP RET;
friend VecT operator%(VecT, T) INT_OP RET;
friend VecT operator&(VecT, T) INT_OP RET;
friend VecT operator|(VecT, T) INT_OP RET;
friend VecT operator^(VecT, T) INT_OP RET;
friend VecT operator%(T, VecT) INT_OP RET;
friend VecT operator&(T, VecT) INT_OP RET;
friend VecT operator|(T, VecT) INT_OP RET;
friend VecT operator^(T, VecT) INT_OP RET;
friend VecT operator%=(VecT, VecT) INT_OP RET;
friend VecT operator&=(VecT, VecT) INT_OP RET;
friend VecT operator|=(VecT, VecT) INT_OP RET;
friend VecT operator^=(VecT, VecT) INT_OP RET;
friend VecT operator%=(VecT, T) INT_OP RET;
friend VecT operator&=(VecT, T) INT_OP RET;
friend VecT operator|=(VecT, T) INT_OP RET;
friend VecT operator^=(VecT, T) INT_OP RET;
friend VecT operator<<(VecT, VecT) INT_OP RET;
friend VecT operator>>(VecT, VecT) INT_OP RET;
friend VecT operator<<=(VecT, VecT) INT_OP RET;
friend VecT operator>>=(VecT, VecT) INT_OP RET;
friend VecT operator<<(T, VecT) INT_OP RET;
friend VecT operator>>(T, VecT) INT_OP RET;
friend VecT operator<<=(T, VecT) INT_OP RET;
friend VecT operator>>=(T, VecT) INT_OP RET;
friend VecT operator<<(VecT, T) INT_OP RET;
friend VecT operator>>(VecT, T) INT_OP RET;
friend VecT operator<<=(VecT, T) INT_OP RET;
friend VecT operator>>=(VecT, T) INT_OP RET;
#undef INT_OP
};
template<typename T, int Sz> struct SwizzleBase : VecOp<T, Sz> {
using VecT = VecBase<T, Sz>;
SwizzleBase() = default;
SwizzleBase(T) {}
constexpr VecT operator=(const VecT &) RET;
operator VecT() const RET;
VecT operator()() const RET;
};
#define SWIZZLE_XY(T) \
SwizzleBase<T, 2> xx, xy, yx, yy; \
SwizzleBase<T, 3> xxx, xxy, xyx, xyy, yxx, yxy, yyx, yyy; \
SwizzleBase<T, 4> xxxx, xxxy, xxyx, xxyy, xyxx, xyxy, xyyx, xyyy, yxxx, yxxy, yxyx, yxyy, yyxx, \
yyxy, yyyx, yyyy;
#define SWIZZLE_RG(T) \
SwizzleBase<T, 2> rr, rg, gr, gg; \
SwizzleBase<T, 3> rrr, rrg, rgr, rgg, grr, grg, ggr, ggg; \
SwizzleBase<T, 4> rrrr, rrrg, rrgr, rrgg, rgrr, rgrg, rggr, rggg, grrr, grrg, grgr, grgg, ggrr, \
ggrg, gggr, gggg;
#define SWIZZLE_XYZ(T) \
SWIZZLE_XY(T) \
SwizzleBase<T, 2> xz, yz, zx, zy, zz; \
SwizzleBase<T, 3> xxz, xyz, xzx, xzy, xzz, yxz, yyz, yzx, yzy, yzz, zxx, zxy, zxz, zyx, zyy, \
zyz, zzx, zzy, zzz; \
SwizzleBase<T, 4> xxxz, xxyz, xxzx, xxzy, xxzz, xyxz, xyyz, xyzx, xyzy, xyzz, xzxx, xzxy, xzxz, \
xzyx, xzyy, xzyz, xzzx, xzzy, xzzz, yxxz, yxyz, yxzx, yxzy, yxzz, yyxz, yyyz, yyzx, yyzy, \
yyzz, yzxx, yzxy, yzxz, yzyx, yzyy, yzyz, yzzx, yzzy, yzzz, zxxx, zxxy, zxxz, zxyx, zxyy, \
zxyz, zxzx, zxzy, zxzz, zyxx, zyxy, zyxz, zyyx, zyyy, zyyz, zyzx, zyzy, zyzz, zzxx, zzxy, \
zzxz, zzyx, zzyy, zzyz, zzzx, zzzy, zzzz;
#define SWIZZLE_RGB(T) \
SWIZZLE_RG(T) \
SwizzleBase<T, 2> rb, gb, br, bg, bb; \
SwizzleBase<T, 3> rrb, rgb, rbr, rbg, rbb, grb, ggb, gbr, gbg, gbb, brr, brg, brb, bgr, bgg, \
bgb, bbr, bbg, bbb; \
SwizzleBase<T, 4> rrrb, rrgb, rrbr, rrbg, rrbb, rgrb, rggb, rgbr, rgbg, rgbb, rbrr, rbrg, rbrb, \
rbgr, rbgg, rbgb, rbbr, rbbg, rbbb, grrb, grgb, grbr, grbg, grbb, ggrb, gggb, ggbr, ggbg, \
ggbb, gbrr, gbrg, gbrb, gbgr, gbgg, gbgb, gbbr, gbbg, gbbb, brrr, brrg, brrb, brgr, brgg, \
brgb, brbr, brbg, brbb, bgrr, bgrg, bgrb, bggr, bggg, bggb, bgbr, bgbg, bgbb, bbrr, bbrg, \
bbrb, bbgr, bbgg, bbgb, bbbr, bbbg, bbbb;
#define SWIZZLE_XYZW(T) \
SWIZZLE_XYZ(T) \
SwizzleBase<T, 2> xw, yw, zw, wx, wy, wz, ww; \
SwizzleBase<T, 3> xxw, xyw, xzw, xwx, xwy, xwz, xww, yxw, yyw, yzw, ywx, ywy, ywz, yww, zxw, \
zyw, zzw, zwx, zwy, zwz, zww, wxx, wxy, wxz, wxw, wyx, wyy, wyz, wyw, wzx, wzy, wzz, wzw, \
wwx, wwy, wwz, www; \
SwizzleBase<T, 4> xxxw, xxyw, xxzw, xxwx, xxwy, xxwz, xxww, xyxw, xyyw, xyzw, xywx, xywy, xywz, \
xyww, xzxw, xzyw, xzzw, xzwx, xzwy, xzwz, xzww, xwxx, xwxy, xwxz, xwxw, xwyx, xwyy, xwyz, \
xwyw, xwzx, xwzy, xwzz, xwzw, xwwx, xwwy, xwwz, xwww, yxxw, yxyw, yxzw, yxwx, yxwy, yxwz, \
yxww, yyxw, yyyw, yyzw, yywx, yywy, yywz, yyww, yzxw, yzyw, yzzw, yzwx, yzwy, yzwz, yzww, \
ywxx, ywxy, ywxz, ywxw, ywyx, ywyy, ywyz, ywyw, ywzx, ywzy, ywzz, ywzw, ywwx, ywwy, ywwz, \
ywww, zxxw, zxyw, zxzw, zxwx, zxwy, zxwz, zxww, zyxw, zyyw, zyzw, zywx, zywy, zywz, zyww, \
zzxw, zzyw, zzzw, zzwx, zzwy, zzwz, zzww, zwxx, zwxy, zwxz, zwxw, zwyx, zwyy, zwyz, zwyw, \
zwzx, zwzy, zwzz, zwzw, zwwx, zwwy, zwwz, zwww, wxxx, wxxy, wxxz, wxxw, wxyx, wxyy, wxyz, \
wxyw, wxzx, wxzy, wxzz, wxzw, wxwx, wxwy, wxwz, wxww, wyxx, wyxy, wyxz, wyxw, wyyx, wyyy, \
wyyz, wyyw, wyzx, wyzy, wyzz, wyzw, wywx, wywy, wywz, wyww, wzxx, wzxy, wzxz, wzxw, wzyx, \
wzyy, wzyz, wzyw, wzzx, wzzy, wzzz, wzzw, wzwx, wzwy, wzwz, wzww, wwxx, wwxy, wwxz, wwxw, \
wwyx, wwyy, wwyz, wwyw, wwzx, wwzy, wwzz, wwzw, wwwx, wwwy, wwwz, wwww;
#define SWIZZLE_RGBA(T) \
SWIZZLE_RGB(T) \
SwizzleBase<T, 2> ra, ga, ba, ar, ag, ab, aa; \
SwizzleBase<T, 3> rra, rga, rba, rar, rag, rab, raa, gra, gga, gba, gar, gag, gab, gaa, bra, \
bga, bba, bar, bag, bab, baa, arr, arg, arb, ara, agr, agg, agb, aga, abr, abg, abb, aba, \
aar, aag, aab, aaa; \
SwizzleBase<T, 4> rrra, rrga, rrba, rrar, rrag, rrab, rraa, rgra, rgga, rgba, rgar, rgag, rgab, \
rgaa, rbra, rbga, rbba, rbar, rbag, rbab, rbaa, rarr, rarg, rarb, rara, ragr, ragg, ragb, \
raga, rabr, rabg, rabb, raba, raar, raag, raab, raaa, grra, grga, grba, grar, grag, grab, \
graa, ggra, ggga, ggba, ggar, ggag, ggab, ggaa, gbra, gbga, gbba, gbar, gbag, gbab, gbaa, \
garr, garg, garb, gara, gagr, gagg, gagb, gaga, gabr, gabg, gabb, gaba, gaar, gaag, gaab, \
gaaa, brra, brga, brba, brar, brag, brab, braa, bgra, bgga, bgba, bgar, bgag, bgab, bgaa, \
bbra, bbga, bbba, bbar, bbag, bbab, bbaa, barr, barg, barb, bara, bagr, bagg, bagb, baga, \
babr, babg, babb, baba, baar, baag, baab, baaa, arrr, arrg, arrb, arra, argr, argg, argb, \
arga, arbr, arbg, arbb, arba, arar, arag, arab, araa, agrr, agrg, agrb, agra, aggr, aggg, \
aggb, agga, agbr, agbg, agbb, agba, agar, agag, agab, agaa, abrr, abrg, abrb, abra, abgr, \
abgg, abgb, abga, abbr, abbg, abbb, abba, abar, abag, abab, abaa, aarr, aarg, aarb, aara, \
aagr, aagg, aagb, aaga, aabr, aabg, aabb, aaba, aaar, aaag, aaab, aaaa;
template<typename T> struct VecBase<T, 1> {
VecBase() = default;
template<typename U> explicit VecBase(VecOp<U, 1>) {}
VecBase(T) {}
operator T() RET;
};
template<typename T> struct VecBase<T, 2> : VecOp<T, 2> {
private:
/* Weird non-zero value to avoid error about division by zero in constexpr. */
static constexpr T V = T(0.123f);
public:
union {
struct {
T x, y;
};
struct {
T r, g;
};
SWIZZLE_XY(T);
SWIZZLE_RG(T);
};
VecBase() = default;
template<typename U> explicit VecBase(VecOp<U, 2>) {}
constexpr explicit VecBase(T) : x(V), y(V) {}
/* Implemented correctly for GCC to compile the constexpr float2 arrays. */
constexpr explicit VecBase(T x_, T y_) : x(x_), y(y_) {}
};
template<typename T> struct VecBase<T, 3> : VecOp<T, 3> {
private:
/* Weird non-zero value to avoid error about division by zero in constexpr. */
static constexpr T V = T(0.123f);
public:
union {
struct {
T x, y, z;
};
struct {
T r, g, b;
};
SWIZZLE_XYZ(T);
SWIZZLE_RGB(T);
};
VecBase() = default;
template<typename U> explicit VecBase(VecOp<U, 3>) {}
constexpr explicit VecBase(T) : x(V), y(V), z(V) {}
/* Implemented correctly for GCC to compile the constexpr gl_WorkGroupSize. */
constexpr explicit VecBase(T x_, T y_, T z_) : x(x_), y(y_), z(z_) {}
constexpr explicit VecBase(VecOp<T, 2>, T) : x(V), y(V), z(V) {}
constexpr explicit VecBase(T, VecOp<T, 2>) : x(V), y(V), z(V) {}
};
template<typename T> struct VecBase<T, 4> : VecOp<T, 4> {
private:
/* Weird non-zero value to avoid error about division by zero in constexpr. */
static constexpr T V = T(0.123f);
public:
union {
struct {
T x, y, z, w;
};
struct {
T r, g, b, a;
};
SWIZZLE_XYZW(T);
SWIZZLE_RGBA(T);
};
VecBase() = default;
template<typename U> explicit VecBase(VecOp<U, 4>) {}
constexpr explicit VecBase(T) : x(V), y(V), z(V), w(V) {}
/* Implemented correctly for GCC to compile the constexpr. */
constexpr explicit VecBase(T x_, T y_, T z_, T w_) : x(x_), y(y_), z(z_), w(w_) {}
constexpr explicit VecBase(VecOp<T, 2>, T, T) : x(V), y(V), z(V), w(V) {}
constexpr explicit VecBase(T, VecOp<T, 2>, T) : x(V), y(V), z(V), w(V) {}
constexpr explicit VecBase(T, T, VecOp<T, 2>) : x(V), y(V), z(V), w(V) {}
constexpr explicit VecBase(VecOp<T, 2>, VecOp<T, 2>) : x(V), y(V), z(V), w(V) {}
constexpr explicit VecBase(VecOp<T, 3>, T) : x(V), y(V), z(V), w(V) {}
constexpr explicit VecBase(T, VecOp<T, 3>) : x(V), y(V), z(V), w(V) {}
};
/* Boolean vectors do not have operators and are not convertible from other types. */
template<> struct VecBase<bool, 2> : VecOp<bool, 2> {
union {
struct {
bool x, y;
};
SWIZZLE_XY(bool);
};
VecBase() = default;
explicit VecBase(bool) {}
explicit VecBase(bool, bool) {}
/* Should be forbidden, but is used by SMAA. */
explicit VecBase(VecOp<float, 2>) {}
};
template<> struct VecBase<bool, 3> : VecOp<bool, 3> {
union {
struct {
bool x, y, z;
};
SWIZZLE_XYZ(bool);
};
VecBase() = default;
explicit VecBase(bool) {}
explicit VecBase(bool, bool, bool) {}
explicit VecBase(VecOp<bool, 2>, bool) {}
explicit VecBase(bool, VecOp<bool, 2>) {}
};
template<> struct VecBase<bool, 4> : VecOp<bool, 4> {
union {
struct {
bool x, y, z, w;
};
SWIZZLE_XYZW(bool);
};
VecBase() = default;
explicit VecBase(bool) {}
explicit VecBase(bool, bool, bool, bool) {}
explicit VecBase(VecOp<bool, 2>, bool, bool) {}
explicit VecBase(bool, VecOp<bool, 2>, bool) {}
explicit VecBase(bool, bool, VecOp<bool, 2>) {}
explicit VecBase(VecOp<bool, 2>, VecOp<bool, 2>) {}
explicit VecBase(VecOp<bool, 3>, bool) {}
explicit VecBase(bool, VecOp<bool, 3>) {}
};
using uint = unsigned int;
using uint32_t = unsigned int; /* For typed enums. */
using float2 = VecBase<float, 2>;
using float3 = VecBase<float, 3>;
using float4 = VecBase<float, 4>;
using uint2 = VecBase<uint, 2>;
using uint3 = VecBase<uint, 3>;
using uint4 = VecBase<uint, 4>;
using int2 = VecBase<int, 2>;
using int3 = VecBase<int, 3>;
using int4 = VecBase<int, 4>;
using uchar = unsigned int;
using uchar2 = VecBase<uchar, 2>;
using uchar3 = VecBase<uchar, 3>;
using uchar4 = VecBase<uchar, 4>;
using char2 = VecBase<char, 2>;
using char3 = VecBase<char, 3>;
using char4 = VecBase<char, 4>;
using ushort = unsigned short;
using ushort2 = VecBase<ushort, 2>;
using ushort3 = VecBase<ushort, 3>;
using ushort4 = VecBase<ushort, 4>;
using short2 = VecBase<short, 2>;
using short3 = VecBase<short, 3>;
using short4 = VecBase<short, 4>;
using half = float;
using half2 = VecBase<half, 2>;
using half3 = VecBase<half, 3>;
using half4 = VecBase<half, 4>;
using bool2 = VecBase<bool, 2>;
using bool3 = VecBase<bool, 3>;
using bool4 = VecBase<bool, 4>;
using bool32_t = uint;
/** Packed types are needed for MSL which have different alignment rules for float3. */
using packed_float2 = float2;
using packed_float3 = float3;
using packed_float4 = float4;
using packed_int2 = int2;
using packed_int3 = int3;
using packed_int4 = int4;
using packed_uint2 = uint2;
using packed_uint3 = uint3;
using packed_uint4 = uint4;
/** \} */
#undef RET

View File

@@ -0,0 +1,11 @@
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_3D_depth_only_infos.hh"
void main()
{
/* No color output, only depth (line below is implicit). */
// gl_FragDepth = gl_FragCoord.z;
}

View File

@@ -0,0 +1,19 @@
/* SPDX-FileCopyrightText: 2017-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_2D_diag_stripes_infos.hh"
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_2D_diag_stripes)
void main()
{
float phase = mod((gl_FragCoord.x + gl_FragCoord.y), float(size1 + size2));
if (phase < size1) {
fragColor = color1;
}
else {
fragColor = color2;
}
}

View File

@@ -0,0 +1,12 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_2D_image_overlays_merge_infos.hh"
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_cycles_display_fallback)
void main()
{
fragColor = texture(image_texture, texCoord_interp);
}

View File

@@ -0,0 +1,18 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_2D_image_overlays_merge_infos.hh"
VERTEX_SHADER_CREATE_INFO(gpu_shader_cycles_display_fallback)
float2 normalize_coordinates()
{
return (float2(2.0f) * (pos / fullscreen)) - float2(1.0f);
}
void main()
{
gl_Position = float4(normalize_coordinates(), 0.0f, 1.0f);
texCoord_interp = texCoord;
}

View File

@@ -0,0 +1,15 @@
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_3D_flat_color_infos.hh"
#include "gpu_shader_colorspace_lib.glsl"
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_3D_flat_color)
void main()
{
fragColor = finalColor;
fragColor = blender_srgb_to_framebuffer_space(fragColor);
}

View File

@@ -0,0 +1,40 @@
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Always start a shader with #version directive and the required #extension.
* The extensions are different depending on the backend and implementation support.
* For this reason this file is generated at runtime.
* The present implementation is just for reference and documentation.
*/
#pragma once
#pragma runtime_generated
#if defined(GPU_OPENGL)
/* We only require OpenGL 4.3. */
# version 430
/* Required: For draw call batching. */
# extension GL_ARB_shader_draw_parameters : enable
/* Optional: Avoid geometry shader for layered rendering. */
# extension GL_ARB_shader_viewport_layer_array : enable
/* Optional: Avoid geometry shader for barycentric coordinates. */
# extension GL_AMD_shader_explicit_vertex_parameter : enable
/* Optional: For sub-pass input emulation. */
# extension GL_EXT_shader_framebuffer_fetch : enable
/* Optional: For faster EEVEE GBuffer classification. */
# extension GL_ARB_shader_stencil_export : enable
#elif defined(GPU_VULKAN)
# version 450
/* Required: For draw call batching. */
# extension GL_ARB_shader_draw_parameters : enable
/* Optional: Avoid geometry shader for layered rendering. */
# extension GL_ARB_shader_viewport_layer_array : enable
/* Optional: Avoid geometry shader for barycentric coordinates. */
# extension GL_EXT_fragment_shader_barycentric : require
/* Optional: For faster EEVEE GBuffer classification. */
# extension GL_ARB_shader_stencil_export : enable
#endif

Some files were not shown because too many files have changed in this diff Show More