Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/gpu')
-rw-r--r--source/blender/gpu/CMakeLists.txt24
-rw-r--r--source/blender/gpu/GPU_common_types.h18
-rw-r--r--source/blender/gpu/GPU_legacy_stubs.h2
-rw-r--r--source/blender/gpu/GPU_shader.h10
-rw-r--r--source/blender/gpu/GPU_state.h13
-rw-r--r--source/blender/gpu/GPU_texture.h4
-rw-r--r--source/blender/gpu/GPU_vertex_buffer.h1
-rw-r--r--source/blender/gpu/GPU_vertex_format.h2
-rw-r--r--source/blender/gpu/intern/gpu_debug.cc6
-rw-r--r--source/blender/gpu/intern/gpu_immediate.cc2
-rw-r--r--source/blender/gpu/intern/gpu_material.c2
-rw-r--r--source/blender/gpu/intern/gpu_shader_builtin.c5
-rw-r--r--source/blender/gpu/intern/gpu_shader_dependency.cc4
-rw-r--r--source/blender/gpu/intern/gpu_vertex_buffer.cc15
-rw-r--r--source/blender/gpu/intern/gpu_vertex_buffer_private.hh1
-rw-r--r--source/blender/gpu/intern/gpu_vertex_format.cc22
-rw-r--r--source/blender/gpu/metal/mtl_context.hh306
-rw-r--r--source/blender/gpu/metal/mtl_context.mm236
-rw-r--r--source/blender/gpu/metal/mtl_state.hh73
-rw-r--r--source/blender/gpu/metal/mtl_state.mm675
-rw-r--r--source/blender/gpu/metal/mtl_texture.hh49
-rw-r--r--source/blender/gpu/metal/mtl_texture.mm8
-rw-r--r--source/blender/gpu/opengl/gl_texture.hh2
-rw-r--r--source/blender/gpu/opengl/gl_vertex_array.cc4
-rw-r--r--source/blender/gpu/opengl/gl_vertex_buffer.cc14
-rw-r--r--source/blender/gpu/opengl/gl_vertex_buffer.hh5
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_color_ramp.glsl (renamed from source/blender/gpu/shaders/material/gpu_shader_material_color_ramp.glsl)0
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl (renamed from source/blender/gpu/shaders/material/gpu_shader_material_color_util.glsl)50
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl162
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_hash.glsl (renamed from source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl)0
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_math.glsl (renamed from source/blender/gpu/shaders/material/gpu_shader_material_math.glsl)2
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl (renamed from source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl)72
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl (renamed from source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl)2
-rw-r--r--source/blender/gpu/shaders/gpu_shader_geometry.glsl93
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh20
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_combine_color.glsl16
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl33
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_rgb_curves.glsl73
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_separate_color.glsl28
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl4
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl4
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_transform_utils.glsl71
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl41
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl2
-rw-r--r--source/blender/gpu/tests/gpu_shader_builtin_test.cc1
62 files changed, 1782 insertions, 429 deletions
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index 7c6390971dd..f963d0e3b39 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -85,6 +85,7 @@ set(SRC
GPU_buffers.h
GPU_capabilities.h
GPU_common.h
+ GPU_common_types.h
GPU_compute.h
GPU_context.h
GPU_debug.h
@@ -189,6 +190,7 @@ set(METAL_SRC
metal/mtl_backend.mm
metal/mtl_context.mm
metal/mtl_debug.mm
+ metal/mtl_state.mm
metal/mtl_texture.mm
metal/mtl_texture_util.mm
@@ -197,6 +199,7 @@ set(METAL_SRC
metal/mtl_common.hh
metal/mtl_context.hh
metal/mtl_debug.hh
+ metal/mtl_state.hh
metal/mtl_texture.hh
)
@@ -301,7 +304,13 @@ set(GLSL_SRC
shaders/gpu_shader_codegen_lib.glsl
- shaders/gpu_shader_geometry.glsl
+ shaders/common/gpu_shader_common_color_ramp.glsl
+ shaders/common/gpu_shader_common_color_utils.glsl
+ shaders/common/gpu_shader_common_curves.glsl
+ shaders/common/gpu_shader_common_hash.glsl
+ shaders/common/gpu_shader_common_math.glsl
+ shaders/common/gpu_shader_common_math_utils.glsl
+ shaders/common/gpu_shader_common_mix_rgb.glsl
shaders/material/gpu_shader_material_add_shader.glsl
shaders/material/gpu_shader_material_ambient_occlusion.glsl
@@ -315,8 +324,7 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_bump.glsl
shaders/material/gpu_shader_material_camera.glsl
shaders/material/gpu_shader_material_clamp.glsl
- shaders/material/gpu_shader_material_color_ramp.glsl
- shaders/material/gpu_shader_material_color_util.glsl
+ shaders/material/gpu_shader_material_combine_color.glsl
shaders/material/gpu_shader_material_combine_hsv.glsl
shaders/material/gpu_shader_material_combine_rgb.glsl
shaders/material/gpu_shader_material_combine_xyz.glsl
@@ -324,7 +332,6 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_displacement.glsl
shaders/material/gpu_shader_material_eevee_specular.glsl
shaders/material/gpu_shader_material_emission.glsl
- shaders/material/gpu_shader_material_float_curve.glsl
shaders/material/gpu_shader_material_fractal_noise.glsl
shaders/material/gpu_shader_material_fresnel.glsl
shaders/material/gpu_shader_material_gamma.glsl
@@ -333,7 +340,6 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_glossy.glsl
shaders/material/gpu_shader_material_hair_info.glsl
shaders/material/gpu_shader_material_hair.glsl
- shaders/material/gpu_shader_material_hash.glsl
shaders/material/gpu_shader_material_holdout.glsl
shaders/material/gpu_shader_material_hue_sat_val.glsl
shaders/material/gpu_shader_material_invert.glsl
@@ -342,9 +348,6 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_light_path.glsl
shaders/material/gpu_shader_material_mapping.glsl
shaders/material/gpu_shader_material_map_range.glsl
- shaders/material/gpu_shader_material_math.glsl
- shaders/material/gpu_shader_material_math_util.glsl
- shaders/material/gpu_shader_material_mix_rgb.glsl
shaders/material/gpu_shader_material_mix_shader.glsl
shaders/material/gpu_shader_material_noise.glsl
shaders/material/gpu_shader_material_normal.glsl
@@ -357,8 +360,8 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_point_info.glsl
shaders/material/gpu_shader_material_principled.glsl
shaders/material/gpu_shader_material_refraction.glsl
- shaders/material/gpu_shader_material_rgb_curves.glsl
shaders/material/gpu_shader_material_rgb_to_bw.glsl
+ shaders/material/gpu_shader_material_separate_color.glsl
shaders/material/gpu_shader_material_separate_hsv.glsl
shaders/material/gpu_shader_material_separate_rgb.glsl
shaders/material/gpu_shader_material_separate_xyz.glsl
@@ -381,10 +384,10 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_tex_wave.glsl
shaders/material/gpu_shader_material_tex_white_noise.glsl
shaders/material/gpu_shader_material_toon.glsl
+ shaders/material/gpu_shader_material_transform_utils.glsl
shaders/material/gpu_shader_material_translucent.glsl
shaders/material/gpu_shader_material_transparent.glsl
shaders/material/gpu_shader_material_uv_map.glsl
- shaders/material/gpu_shader_material_vector_curves.glsl
shaders/material/gpu_shader_material_vector_displacement.glsl
shaders/material/gpu_shader_material_vector_math.glsl
shaders/material/gpu_shader_material_vector_rotate.glsl
@@ -496,6 +499,7 @@ set(SRC_SHADER_CREATE_INFOS
shaders/infos/gpu_shader_2D_widget_info.hh
shaders/infos/gpu_shader_3D_depth_only_info.hh
shaders/infos/gpu_shader_3D_flat_color_info.hh
+ shaders/infos/gpu_shader_3D_image_info.hh
shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh
shaders/infos/gpu_shader_3D_point_info.hh
shaders/infos/gpu_shader_3D_polyline_info.hh
diff --git a/source/blender/gpu/GPU_common_types.h b/source/blender/gpu/GPU_common_types.h
new file mode 100644
index 00000000000..e08143c2449
--- /dev/null
+++ b/source/blender/gpu/GPU_common_types.h
@@ -0,0 +1,18 @@
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum eGPUFrontFace {
+ GPU_CLOCKWISE,
+ GPU_COUNTERCLOCKWISE,
+} eGPUFrontFace;
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/source/blender/gpu/GPU_legacy_stubs.h b/source/blender/gpu/GPU_legacy_stubs.h
index 369347447f8..5970738a9b3 100644
--- a/source/blender/gpu/GPU_legacy_stubs.h
+++ b/source/blender/gpu/GPU_legacy_stubs.h
@@ -23,7 +23,7 @@
#include "BLI_utildefines.h"
/**
- * Empty function, use for breakpoint when a deprecated
+ * Empty function, use for break-point when a deprecated
* OpenGL function is called.
*/
static void gl_deprecated(void)
diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h
index b620fe9cc9d..3460d33fe68 100644
--- a/source/blender/gpu/GPU_shader.h
+++ b/source/blender/gpu/GPU_shader.h
@@ -280,6 +280,16 @@ typedef enum eGPUBuiltinShader {
GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE,
GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR,
/**
+ * Draw a texture in 3D. Take a 3D position and a 2D texture coordinate for each vertex.
+ *
+ * Exposed via Python-API for add-ons.
+ *
+ * \param image: uniform sampler2D
+ * \param texCoord: in vec2
+ * \param pos: in vec3
+ */
+ GPU_SHADER_3D_IMAGE,
+ /**
* Draw texture with alpha. Take a 3D position and a 2D texture coordinate for each vertex.
*
* \param alpha: uniform float
diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h
index 99b60351dcc..7519b3bc1cb 100644
--- a/source/blender/gpu/GPU_state.h
+++ b/source/blender/gpu/GPU_state.h
@@ -35,6 +35,19 @@ typedef enum eGPUBarrier {
ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_ELEMENT_ARRAY)
+/* NOTE: For Metal and Vulkan only.
+ * TODO(Metal): Update barrier calls to use stage flags. */
+typedef enum eGPUStageBarrierBits {
+ GPU_BARRIER_STAGE_VERTEX = (1 << 0),
+ GPU_BARRIER_STAGE_FRAGMENT = (1 << 1),
+ GPU_BARRIER_STAGE_COMPUTE = (1 << 2),
+ GPU_BARRIER_STAGE_ANY_GRAPHICS = (GPU_BARRIER_STAGE_VERTEX | GPU_BARRIER_STAGE_FRAGMENT),
+ GPU_BARRIER_STAGE_ANY = (GPU_BARRIER_STAGE_VERTEX | GPU_BARRIER_STAGE_FRAGMENT |
+ GPU_BARRIER_STAGE_COMPUTE),
+} eGPUStageBarrierBits;
+
+ENUM_OPERATORS(eGPUStageBarrierBits, GPU_BARRIER_STAGE_COMPUTE)
+
/**
* Defines the fixed pipeline blending equation.
* SRC is the output color from the shader.
diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h
index bb0912f284b..b045d908438 100644
--- a/source/blender/gpu/GPU_texture.h
+++ b/source/blender/gpu/GPU_texture.h
@@ -280,9 +280,9 @@ void *GPU_texture_read(GPUTexture *tex, eGPUDataFormat data_format, int miplvl);
/**
* Fills the whole texture with the same data for all pixels.
* \warning Only work for 2D texture for now.
- * \warning Only clears the mip 0 of the texture.
+ * \warning Only clears the MIP 0 of the texture.
* \param data_format: data format of the pixel data.
- * \note The format is float for unorm textures.
+ * \note The format is float for UNORM textures.
* \param data: 1 pixel worth of data to fill the texture with.
*/
void GPU_texture_clear(GPUTexture *tex, eGPUDataFormat data_format, const void *data);
diff --git a/source/blender/gpu/GPU_vertex_buffer.h b/source/blender/gpu/GPU_vertex_buffer.h
index fe6ed7eaf87..722ef878271 100644
--- a/source/blender/gpu/GPU_vertex_buffer.h
+++ b/source/blender/gpu/GPU_vertex_buffer.h
@@ -165,6 +165,7 @@ void GPU_vertbuf_tag_dirty(GPUVertBuf *verts);
*/
void GPU_vertbuf_use(GPUVertBuf *);
void GPU_vertbuf_bind_as_ssbo(struct GPUVertBuf *verts, int binding);
+void GPU_vertbuf_bind_as_texture(struct GPUVertBuf *verts, int binding);
void GPU_vertbuf_wrap_handle(GPUVertBuf *verts, uint64_t handle);
diff --git a/source/blender/gpu/GPU_vertex_format.h b/source/blender/gpu/GPU_vertex_format.h
index c6b0d5902fe..bf8dc409cb7 100644
--- a/source/blender/gpu/GPU_vertex_format.h
+++ b/source/blender/gpu/GPU_vertex_format.h
@@ -55,7 +55,7 @@ typedef struct GPUVertAttr {
/* 1 to 4 or 8 or 12 or 16 */
uint comp_len : 5;
/* size in bytes, 1 to 64 */
- uint sz : 7;
+ uint size : 7;
/* from beginning of vertex, in bytes */
uint offset : 11;
/* up to GPU_VERT_ATTR_MAX_NAMES */
diff --git a/source/blender/gpu/intern/gpu_debug.cc b/source/blender/gpu/intern/gpu_debug.cc
index c62a6416f92..055207eace8 100644
--- a/source/blender/gpu/intern/gpu_debug.cc
+++ b/source/blender/gpu/intern/gpu_debug.cc
@@ -50,11 +50,11 @@ void GPU_debug_get_groups_names(int name_buf_len, char *r_name_buf)
r_name_buf[0] = '\0';
return;
}
- size_t sz = 0;
+ size_t len = 0;
for (StringRef &name : stack) {
- sz += BLI_snprintf_rlen(r_name_buf + sz, name_buf_len - sz, "%s > ", name.data());
+ len += BLI_snprintf_rlen(r_name_buf + len, name_buf_len - len, "%s > ", name.data());
}
- r_name_buf[sz - 3] = '\0';
+ r_name_buf[len - 3] = '\0';
}
bool GPU_debug_group_match(const char *ref)
diff --git a/source/blender/gpu/intern/gpu_immediate.cc b/source/blender/gpu/intern/gpu_immediate.cc
index 7dc1c739750..69467e5b28a 100644
--- a/source/blender/gpu/intern/gpu_immediate.cc
+++ b/source/blender/gpu/intern/gpu_immediate.cc
@@ -490,7 +490,7 @@ static void immEndVertex() /* and move on to the next vertex */
#endif
uchar *data = imm->vertex_data + a->offset;
- memcpy(data, data - imm->vertex_format.stride, a->sz);
+ memcpy(data, data - imm->vertex_format.stride, a->size);
/* TODO: consolidate copy of adjacent attributes */
}
}
diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c
index 7ec6ee5183a..23028f58059 100644
--- a/source/blender/gpu/intern/gpu_material.c
+++ b/source/blender/gpu/intern/gpu_material.c
@@ -626,7 +626,7 @@ eGPUMaterialFlag GPU_material_flag(const GPUMaterial *mat)
return mat->flag;
}
-/* Note: Consumes the flags. */
+/* NOTE: Consumes the flags. */
bool GPU_material_recalc_flag_get(GPUMaterial *mat)
{
bool updated = (mat->flag & GPU_MATFLAG_UPDATED) != 0;
diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c
index 1100272b712..b92fae4a89b 100644
--- a/source/blender/gpu/intern/gpu_shader_builtin.c
+++ b/source/blender/gpu/intern/gpu_shader_builtin.c
@@ -150,6 +150,11 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = {
.name = "GPU_SHADER_SIMPLE_LIGHTING",
.create_info = "gpu_shader_simple_lighting",
},
+ [GPU_SHADER_3D_IMAGE] =
+ {
+ .name = "GPU_SHADER_3D_IMAGE",
+ .create_info = "gpu_shader_3D_image",
+ },
[GPU_SHADER_3D_IMAGE_MODULATE_ALPHA] =
{
.name = "GPU_SHADER_3D_IMAGE_MODULATE_ALPHA",
diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc
index f69c56b5f3f..aa2033b9154 100644
--- a/source/blender/gpu/intern/gpu_shader_dependency.cc
+++ b/source/blender/gpu/intern/gpu_shader_dependency.cc
@@ -593,7 +593,9 @@ struct GPUSource {
bool is_from_material_library() const
{
- return filename.startswith("gpu_shader_material_") && filename.endswith(".glsl");
+ return (filename.startswith("gpu_shader_material_") ||
+ filename.startswith("gpu_shader_common_")) &&
+ filename.endswith(".glsl");
}
};
diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.cc b/source/blender/gpu/intern/gpu_vertex_buffer.cc
index 2974547c858..f47970d48d1 100644
--- a/source/blender/gpu/intern/gpu_vertex_buffer.cc
+++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc
@@ -199,7 +199,7 @@ void GPU_vertbuf_attr_set(GPUVertBuf *verts_, uint a_idx, uint v_idx, const void
BLI_assert(a_idx < format->attr_len);
BLI_assert(verts->data != nullptr);
verts->flag |= GPU_VERTBUF_DATA_DIRTY;
- memcpy(verts->data + a->offset + v_idx * format->stride, data, a->sz);
+ memcpy(verts->data + a->offset + v_idx * format->stride, data, a->size);
}
void GPU_vertbuf_attr_fill(GPUVertBuf *verts_, uint a_idx, const void *data)
@@ -208,7 +208,7 @@ void GPU_vertbuf_attr_fill(GPUVertBuf *verts_, uint a_idx, const void *data)
const GPUVertFormat *format = &verts->format;
BLI_assert(a_idx < format->attr_len);
const GPUVertAttr *a = &format->attrs[a_idx];
- const uint stride = a->sz; /* tightly packed input data */
+ const uint stride = a->size; /* tightly packed input data */
verts->flag |= GPU_VERTBUF_DATA_DIRTY;
GPU_vertbuf_attr_fill_stride(verts_, a_idx, stride, data);
}
@@ -235,13 +235,13 @@ void GPU_vertbuf_attr_fill_stride(GPUVertBuf *verts_, uint a_idx, uint stride, c
if (format->attr_len == 1 && stride == format->stride) {
/* we can copy it all at once */
- memcpy(verts->data, data, vertex_len * a->sz);
+ memcpy(verts->data, data, vertex_len * a->size);
}
else {
/* we must copy it per vertex */
for (uint v = 0; v < vertex_len; v++) {
memcpy(
- verts->data + a->offset + v * format->stride, (const uchar *)data + v * stride, a->sz);
+ verts->data + a->offset + v * format->stride, (const uchar *)data + v * stride, a->size);
}
}
}
@@ -256,7 +256,7 @@ void GPU_vertbuf_attr_get_raw_data(GPUVertBuf *verts_, uint a_idx, GPUVertBufRaw
verts->flag |= GPU_VERTBUF_DATA_DIRTY;
verts->flag &= ~GPU_VERTBUF_DATA_UPLOADED;
- access->size = a->sz;
+ access->size = a->size;
access->stride = format->stride;
access->data = (uchar *)verts->data + a->offset;
access->data_init = access->data;
@@ -328,6 +328,11 @@ void GPU_vertbuf_bind_as_ssbo(struct GPUVertBuf *verts, int binding)
unwrap(verts)->bind_as_ssbo(binding);
}
+void GPU_vertbuf_bind_as_texture(struct GPUVertBuf *verts, int binding)
+{
+ unwrap(verts)->bind_as_texture(binding);
+}
+
void GPU_vertbuf_update_sub(GPUVertBuf *verts, uint start, uint len, const void *data)
{
unwrap(verts)->update_sub(start, len, data);
diff --git a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh
index e5b70de9dfa..7a0b53cf958 100644
--- a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh
+++ b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh
@@ -51,6 +51,7 @@ class VertBuf {
void resize(uint vert_len);
void upload();
virtual void bind_as_ssbo(uint binding) = 0;
+ virtual void bind_as_texture(uint binding) = 0;
virtual void wrap_handle(uint64_t handle) = 0;
diff --git a/source/blender/gpu/intern/gpu_vertex_format.cc b/source/blender/gpu/intern/gpu_vertex_format.cc
index a9ac191754b..59ae862aa51 100644
--- a/source/blender/gpu/intern/gpu_vertex_format.cc
+++ b/source/blender/gpu/intern/gpu_vertex_format.cc
@@ -49,7 +49,7 @@ void GPU_vertformat_copy(GPUVertFormat *dest, const GPUVertFormat *src)
memcpy(dest, src, sizeof(GPUVertFormat));
}
-static uint comp_sz(GPUVertCompType type)
+static uint comp_size(GPUVertCompType type)
{
#if TRUST_NO_ONE
assert(type <= GPU_COMP_F32); /* other types have irregular sizes (not bytes) */
@@ -58,12 +58,12 @@ static uint comp_sz(GPUVertCompType type)
return sizes[type];
}
-static uint attr_sz(const GPUVertAttr *a)
+static uint attr_size(const GPUVertAttr *a)
{
if (a->comp_type == GPU_COMP_I10) {
return 4; /* always packed as 10_10_10_2 */
}
- return a->comp_len * comp_sz(static_cast<GPUVertCompType>(a->comp_type));
+ return a->comp_len * comp_size(static_cast<GPUVertCompType>(a->comp_type));
}
static uint attr_align(const GPUVertAttr *a)
@@ -71,7 +71,7 @@ static uint attr_align(const GPUVertAttr *a)
if (a->comp_type == GPU_COMP_I10) {
return 4; /* always packed as 10_10_10_2 */
}
- uint c = comp_sz(static_cast<GPUVertCompType>(a->comp_type));
+ uint c = comp_size(static_cast<GPUVertCompType>(a->comp_type));
if (a->comp_len == 3 && c <= 2) {
return 4 * c; /* AMD HW can't fetch these well, so pad it out (other vendors too?) */
}
@@ -156,7 +156,7 @@ uint GPU_vertformat_attr_add(GPUVertFormat *format,
attr->comp_len = (comp_type == GPU_COMP_I10) ?
4 :
comp_len; /* system needs 10_10_10_2 to be 4 or BGRA */
- attr->sz = attr_sz(attr);
+ attr->size = attr_size(attr);
attr->offset = 0; /* offsets & stride are calculated later (during pack) */
attr->fetch_mode = fetch_mode;
@@ -294,13 +294,13 @@ uint padding(uint offset, uint alignment)
}
#if PACK_DEBUG
-static void show_pack(uint a_idx, uint sz, uint pad)
+static void show_pack(uint a_idx, uint size, uint pad)
{
const char c = 'A' + a_idx;
for (uint i = 0; i < pad; i++) {
putchar('-');
}
- for (uint i = 0; i < sz; i++) {
+ for (uint i = 0; i < size; i++) {
putchar(c);
}
}
@@ -310,10 +310,10 @@ void VertexFormat_pack(GPUVertFormat *format)
{
GPUVertAttr *a0 = &format->attrs[0];
a0->offset = 0;
- uint offset = a0->sz;
+ uint offset = a0->size;
#if PACK_DEBUG
- show_pack(0, a0->sz, 0);
+ show_pack(0, a0->size, 0);
#endif
for (uint a_idx = 1; a_idx < format->attr_len; a_idx++) {
@@ -321,10 +321,10 @@ void VertexFormat_pack(GPUVertFormat *format)
uint mid_padding = padding(offset, attr_align(a));
offset += mid_padding;
a->offset = offset;
- offset += a->sz;
+ offset += a->size;
#if PACK_DEBUG
- show_pack(a_idx, a->sz, mid_padding);
+ show_pack(a_idx, a->size, mid_padding);
#endif
}
diff --git a/source/blender/gpu/metal/mtl_context.hh b/source/blender/gpu/metal/mtl_context.hh
index aa198482291..1849a04ea48 100644
--- a/source/blender/gpu/metal/mtl_context.hh
+++ b/source/blender/gpu/metal/mtl_context.hh
@@ -7,8 +7,10 @@
#include "gpu_context_private.hh"
+#include "GPU_common_types.h"
#include "GPU_context.h"
+#include "mtl_capabilities.hh"
#include "mtl_texture.hh"
#include <Cocoa/Cocoa.h>
@@ -21,6 +23,112 @@
namespace blender::gpu {
+class MTLShader;
+class MTLUniformBuf;
+class MTLBuffer;
+
+/* Depth Stencil State */
+typedef struct MTLContextDepthStencilState {
+
+ /* Depth State. */
+ bool depth_write_enable;
+ bool depth_test_enabled;
+ float depth_range_near;
+ float depth_range_far;
+ MTLCompareFunction depth_function;
+ float depth_bias;
+ float depth_slope_scale;
+ bool depth_bias_enabled_for_points;
+ bool depth_bias_enabled_for_lines;
+ bool depth_bias_enabled_for_tris;
+
+ /* Stencil State. */
+ bool stencil_test_enabled;
+ unsigned int stencil_read_mask;
+ unsigned int stencil_write_mask;
+ unsigned int stencil_ref;
+ MTLCompareFunction stencil_func;
+
+ MTLStencilOperation stencil_op_front_stencil_fail;
+ MTLStencilOperation stencil_op_front_depth_fail;
+ MTLStencilOperation stencil_op_front_depthstencil_pass;
+
+ MTLStencilOperation stencil_op_back_stencil_fail;
+ MTLStencilOperation stencil_op_back_depth_fail;
+ MTLStencilOperation stencil_op_back_depthstencil_pass;
+
+ /* Frame-buffer State -- We need to mark this, in case stencil state remains unchanged,
+ * but attachment state has changed. */
+ bool has_depth_target;
+ bool has_stencil_target;
+
+ /* TODO(Metal): Consider optimizing this function using memcmp.
+ * Un-used, but differing, stencil state leads to over-generation
+ * of state objects when doing trivial compare. */
+ inline bool operator==(const MTLContextDepthStencilState &other) const
+ {
+ bool depth_state_equality = (has_depth_target == other.has_depth_target &&
+ depth_write_enable == other.depth_write_enable &&
+ depth_test_enabled == other.depth_test_enabled &&
+ depth_function == other.depth_function);
+
+ bool stencil_state_equality = true;
+ if (has_stencil_target) {
+ stencil_state_equality =
+ (has_stencil_target == other.has_stencil_target &&
+ stencil_test_enabled == other.stencil_test_enabled &&
+ stencil_op_front_stencil_fail == other.stencil_op_front_stencil_fail &&
+ stencil_op_front_depth_fail == other.stencil_op_front_depth_fail &&
+ stencil_op_front_depthstencil_pass == other.stencil_op_front_depthstencil_pass &&
+ stencil_op_back_stencil_fail == other.stencil_op_back_stencil_fail &&
+ stencil_op_back_depth_fail == other.stencil_op_back_depth_fail &&
+ stencil_op_back_depthstencil_pass == other.stencil_op_back_depthstencil_pass &&
+ stencil_func == other.stencil_func && stencil_read_mask == other.stencil_read_mask &&
+ stencil_write_mask == other.stencil_write_mask);
+ }
+
+ return depth_state_equality && stencil_state_equality;
+ }
+
+ /* Depth stencil state will get hashed in order to prepare
+ * MTLDepthStencilState objects. The hash should comprise of
+ * all elements which fill the MTLDepthStencilDescriptor.
+ * These are bound when [rec setDepthStencilState:...] is called.
+ * Depth bias and stencil reference value are set dynamically on the RenderCommandEncoder:
+ * - setStencilReferenceValue:
+ * - setDepthBias:slopeScale:clamp:
+ */
+ inline std::size_t hash() const
+ {
+ std::size_t boolean_bitmask = (this->depth_write_enable ? 1 : 0) |
+ ((this->depth_test_enabled ? 1 : 0) << 1) |
+ ((this->depth_bias_enabled_for_points ? 1 : 0) << 2) |
+ ((this->depth_bias_enabled_for_lines ? 1 : 0) << 3) |
+ ((this->depth_bias_enabled_for_tris ? 1 : 0) << 4) |
+ ((this->stencil_test_enabled ? 1 : 0) << 5) |
+ ((this->has_depth_target ? 1 : 0) << 6) |
+ ((this->has_stencil_target ? 1 : 0) << 7);
+
+ std::size_t stencilop_bitmask = ((std::size_t)this->stencil_op_front_stencil_fail) |
+ ((std::size_t)this->stencil_op_front_depth_fail << 3) |
+ ((std::size_t)this->stencil_op_front_depthstencil_pass << 6) |
+ ((std::size_t)this->stencil_op_back_stencil_fail << 9) |
+ ((std::size_t)this->stencil_op_back_depth_fail << 12) |
+ ((std::size_t)this->stencil_op_back_depthstencil_pass << 15);
+
+ std::size_t main_hash = (std::size_t)this->depth_function;
+ if (this->has_stencil_target) {
+ main_hash += (std::size_t)(this->stencil_read_mask & 0xFF) << 8;
+ main_hash += (std::size_t)(this->stencil_write_mask & 0xFF) << 16;
+ }
+ main_hash ^= (std::size_t)this->stencil_func << 16;
+ main_hash ^= stencilop_bitmask;
+
+ std::size_t final_hash = (main_hash << 8) | boolean_bitmask;
+ return final_hash;
+ }
+} MTLContextDepthStencilState;
+
typedef struct MTLContextTextureUtils {
/* Depth Update Utilities */
@@ -108,11 +216,149 @@ typedef struct MTLContextTextureUtils {
} MTLContextTextureUtils;
+/* Structs containing information on current binding state for textures and samplers. */
+typedef struct MTLTextureBinding {
+ bool used;
+
+ /* Same value as index in bindings array. */
+ unsigned int texture_slot_index;
+ gpu::MTLTexture *texture_resource;
+
+} MTLTextureBinding;
+
+typedef struct MTLSamplerBinding {
+ bool used;
+ MTLSamplerState state;
+
+ bool operator==(MTLSamplerBinding const &other) const
+ {
+ return (used == other.used && state == other.state);
+ }
+} MTLSamplerBinding;
+
+/* Combined sampler state configuration for Argument Buffer caching. */
+struct MTLSamplerArray {
+ unsigned int num_samplers;
+ /* MTLSamplerState permutations between 0..256 - slightly more than a byte. */
+ MTLSamplerState mtl_sampler_flags[MTL_MAX_TEXTURE_SLOTS];
+ id<MTLSamplerState> mtl_sampler[MTL_MAX_TEXTURE_SLOTS];
+
+ inline bool operator==(const MTLSamplerArray &other) const
+ {
+ if (this->num_samplers != other.num_samplers) {
+ return false;
+ }
+ return (memcmp(this->mtl_sampler_flags,
+ other.mtl_sampler_flags,
+ sizeof(MTLSamplerState) * this->num_samplers) == 0);
+ }
+
+ inline uint32_t hash() const
+ {
+ uint32_t hash = this->num_samplers;
+ for (int i = 0; i < this->num_samplers; i++) {
+ hash ^= (uint32_t)this->mtl_sampler_flags[i] << (i % 3);
+ }
+ return hash;
+ }
+};
+
+typedef enum MTLPipelineStateDirtyFlag {
+ MTL_PIPELINE_STATE_NULL_FLAG = 0,
+ /* Whether we need to call setViewport. */
+ MTL_PIPELINE_STATE_VIEWPORT_FLAG = (1 << 0),
+ /* Whether we need to call setScissor.*/
+ MTL_PIPELINE_STATE_SCISSOR_FLAG = (1 << 1),
+ /* Whether we need to update/rebind active depth stencil state. */
+ MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG = (1 << 2),
+ /* Whether we need to update/rebind active PSO. */
+ MTL_PIPELINE_STATE_PSO_FLAG = (1 << 3),
+ /* Whether we need to update the frontFacingWinding state. */
+ MTL_PIPELINE_STATE_FRONT_FACING_FLAG = (1 << 4),
+ /* Whether we need to update the culling state. */
+ MTL_PIPELINE_STATE_CULLMODE_FLAG = (1 << 5),
+ /* Full pipeline state needs applying. Occurs when beginning a new render pass. */
+ MTL_PIPELINE_STATE_ALL_FLAG =
+ (MTL_PIPELINE_STATE_VIEWPORT_FLAG | MTL_PIPELINE_STATE_SCISSOR_FLAG |
+ MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG | MTL_PIPELINE_STATE_PSO_FLAG |
+ MTL_PIPELINE_STATE_FRONT_FACING_FLAG | MTL_PIPELINE_STATE_CULLMODE_FLAG)
+} MTLPipelineStateDirtyFlag;
+
+/* Ignore full flag bit-mask `MTL_PIPELINE_STATE_ALL_FLAG`. */
+ENUM_OPERATORS(MTLPipelineStateDirtyFlag, MTL_PIPELINE_STATE_CULLMODE_FLAG);
+
+typedef struct MTLUniformBufferBinding {
+ bool bound;
+ MTLUniformBuf *ubo;
+} MTLUniformBufferBinding;
+
typedef struct MTLContextGlobalShaderPipelineState {
- /* ..TODO(Metal): More elements to be added as backend fleshed out.. */
+ bool initialised;
+
+ /* Whether the pipeline state has been modified since application.
+ * `dirty_flags` is a bitmask of the types of state which have been updated.
+ * This is in order to optimize calls and only re-apply state as needed.
+ * Some state parameters are dynamically applied on the RenderCommandEncoder,
+ * others may be encapsulated in GPU-resident state objects such as
+ * MTLDepthStencilState or MTLRenderPipelineState. */
+ bool dirty;
+ MTLPipelineStateDirtyFlag dirty_flags;
+
+ /* Shader resources. */
+ MTLShader *null_shader;
+
+ /* Active Shader State. */
+ MTLShader *active_shader;
+
+ /* Global Uniform Buffers. */
+ MTLUniformBufferBinding ubo_bindings[MTL_MAX_UNIFORM_BUFFER_BINDINGS];
+
+ /* Context Texture bindings. */
+ MTLTextureBinding texture_bindings[MTL_MAX_TEXTURE_SLOTS];
+ MTLSamplerBinding sampler_bindings[MTL_MAX_SAMPLER_SLOTS];
+
+ /*** --- Render Pipeline State --- ***/
+ /* Track global render pipeline state for the current context. The functions in GPU_state.h
+ * modify these parameters. Certain values, tagged [PSO], are parameters which are required to be
+ * passed into PSO creation, rather than dynamic state functions on the RenderCommandEncoder.
+ */
- /*** DATA and IMAGE access state ***/
+ /* Blending State. */
+ MTLColorWriteMask color_write_mask; /* [PSO] */
+ bool blending_enabled; /* [PSO] */
+ MTLBlendOperation alpha_blend_op; /* [PSO] */
+ MTLBlendOperation rgb_blend_op; /* [PSO] */
+ MTLBlendFactor dest_alpha_blend_factor; /* [PSO] */
+ MTLBlendFactor dest_rgb_blend_factor; /* [PSO] */
+ MTLBlendFactor src_alpha_blend_factor; /* [PSO] */
+ MTLBlendFactor src_rgb_blend_factor; /* [PSO] */
+
+ /* Culling State. */
+ bool culling_enabled;
+ eGPUFaceCullTest cull_mode;
+ eGPUFrontFace front_face;
+
+ /* Depth State. */
+ MTLContextDepthStencilState depth_stencil_state;
+
+ /* Viewport/Scissor Region. */
+ int viewport_offset_x;
+ int viewport_offset_y;
+ int viewport_width;
+ int viewport_height;
+ bool scissor_enabled;
+ int scissor_x;
+ int scissor_y;
+ int scissor_width;
+ int scissor_height;
+
+ /* Image data access state. */
uint unpack_row_length;
+
+ /* Render parameters. */
+ float point_size = 1.0f;
+ float line_width = 1.0f;
+
} MTLContextGlobalShaderPipelineState;
/* Metal Buffer */
@@ -127,8 +373,8 @@ typedef struct MTLTemporaryBufferRange {
bool requires_flush();
} MTLTemporaryBufferRange;
-/** MTLContext -- Core render loop and state management **/
-/* Note(Metal): Partial MTLContext stub to provide wrapper functionality
+/** MTLContext -- Core render loop and state management. **/
+/* NOTE(Metal): Partial MTLContext stub to provide wrapper functionality
* for work-in-progress MTL* classes. */
class MTLContext : public Context {
@@ -138,8 +384,24 @@ class MTLContext : public Context {
/* Compute and specialization caches. */
MTLContextTextureUtils texture_utils_;
+ /* Texture Samplers. */
+ /* Cache of generated MTLSamplerState objects based on permutations of `eGPUSamplerState`. */
+ id<MTLSamplerState> sampler_state_cache_[GPU_SAMPLER_MAX] = {0};
+ id<MTLSamplerState> default_sampler_state_ = nil;
+
+ /* When texture sampler count exceeds the resource bind limit, an
+ * argument buffer is used to pass samplers to the shader.
+ * Each unique configurations of multiple samplers can be cached, so as to not require
+ * re-generation. `samplers_` stores the current list of bound sampler objects.
+ * `cached_sampler_buffers_` is a cache of encoded argument buffers which can be re-used. */
+ MTLSamplerArray samplers_;
+ blender::Map<MTLSamplerArray, gpu::MTLBuffer *> cached_sampler_buffers_;
+
public:
- /* METAL API Resource Handles. */
+ /* Shaders and Pipeline state. */
+ MTLContextGlobalShaderPipelineState pipeline_state;
+
+ /* Metal API Resource Handles. */
id<MTLCommandQueue> queue = nil;
id<MTLDevice> device = nil;
@@ -160,24 +422,40 @@ class MTLContext : public Context {
void debug_group_begin(const char *name, int index) override;
void debug_group_end(void) override;
- /*** Context Utility functions */
+ /*** MTLContext Utility functions. */
/*
* All below functions modify the global state for the context, controlling the flow of
* rendering, binding resources, setting global state, resource management etc;
*/
- /* Metal Context Core functions */
- /* Command Buffer Management */
+ /* Metal Context Core functions. */
+ /* Command Buffer Management. */
id<MTLCommandBuffer> get_active_command_buffer();
- /* Render Pass State and Management */
+ /* Render Pass State and Management. */
void begin_render_pass();
void end_render_pass();
-
- /* Shaders and Pipeline state */
- MTLContextGlobalShaderPipelineState pipeline_state;
-
- /* Texture utilities */
+ bool is_render_pass_active();
+
+ /* Texture Binding. */
+ void texture_bind(gpu::MTLTexture *mtl_texture, unsigned int texture_unit);
+ void sampler_bind(MTLSamplerState, unsigned int sampler_unit);
+ void texture_unbind(gpu::MTLTexture *mtl_texture);
+ void texture_unbind_all(void);
+ id<MTLSamplerState> get_sampler_from_state(MTLSamplerState state);
+ id<MTLSamplerState> generate_sampler_from_state(MTLSamplerState state);
+ id<MTLSamplerState> get_default_sampler_state();
+
+ /* Metal Context pipeline state. */
+ void pipeline_state_init(void);
+ MTLShader *get_active_shader(void);
+
+ /* State assignment. */
+ void set_viewport(int origin_x, int origin_y, int width, int height);
+ void set_scissor(int scissor_x, int scissor_y, int scissor_width, int scissor_height);
+ void set_scissor_enabled(bool scissor_enabled);
+
+ /* Texture utilities. */
MTLContextTextureUtils &get_texture_utils()
{
return this->texture_utils_;
diff --git a/source/blender/gpu/metal/mtl_context.mm b/source/blender/gpu/metal/mtl_context.mm
index 18ed38c373d..94f5682b11b 100644
--- a/source/blender/gpu/metal/mtl_context.mm
+++ b/source/blender/gpu/metal/mtl_context.mm
@@ -5,6 +5,11 @@
*/
#include "mtl_context.hh"
#include "mtl_debug.hh"
+#include "mtl_state.hh"
+
+#include "DNA_userdef_types.h"
+
+#include "GPU_capabilities.h"
using namespace blender;
using namespace blender::gpu;
@@ -44,6 +49,9 @@ MTLContext::MTLContext(void *ghost_window)
/* Init debug. */
debug::mtl_debug_init();
+ /* Initialize Metal modules. */
+ this->state_manager = new MTLStateManager(this);
+
/* TODO(Metal): Implement. */
}
@@ -98,6 +106,234 @@ void MTLContext::end_render_pass()
/* TODO(Metal): Implement. */
}
+bool MTLContext::is_render_pass_active()
+{
+ /* TODO(Metal): Implement. */
+ return false;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Global Context State
+ * \{ */
+
+/* Metal Context Pipeline State. */
+void MTLContext::pipeline_state_init()
+{
+ /*** Initialize state only once. ***/
+ if (!this->pipeline_state.initialised) {
+ this->pipeline_state.initialised = true;
+ this->pipeline_state.active_shader = NULL;
+
+ /* Clear bindings state. */
+ for (int t = 0; t < GPU_max_textures(); t++) {
+ this->pipeline_state.texture_bindings[t].used = false;
+ this->pipeline_state.texture_bindings[t].texture_slot_index = t;
+ this->pipeline_state.texture_bindings[t].texture_resource = NULL;
+ }
+ for (int s = 0; s < MTL_MAX_SAMPLER_SLOTS; s++) {
+ this->pipeline_state.sampler_bindings[s].used = false;
+ }
+ for (int u = 0; u < MTL_MAX_UNIFORM_BUFFER_BINDINGS; u++) {
+ this->pipeline_state.ubo_bindings[u].bound = false;
+ this->pipeline_state.ubo_bindings[u].ubo = NULL;
+ }
+ }
+
+ /*** State defaults -- restored by GPU_state_init. ***/
+ /* Clear blending State. */
+ this->pipeline_state.color_write_mask = MTLColorWriteMaskRed | MTLColorWriteMaskGreen |
+ MTLColorWriteMaskBlue | MTLColorWriteMaskAlpha;
+ this->pipeline_state.blending_enabled = false;
+ this->pipeline_state.alpha_blend_op = MTLBlendOperationAdd;
+ this->pipeline_state.rgb_blend_op = MTLBlendOperationAdd;
+ this->pipeline_state.dest_alpha_blend_factor = MTLBlendFactorZero;
+ this->pipeline_state.dest_rgb_blend_factor = MTLBlendFactorZero;
+ this->pipeline_state.src_alpha_blend_factor = MTLBlendFactorOne;
+ this->pipeline_state.src_rgb_blend_factor = MTLBlendFactorOne;
+
+ /* Viewport and scissor. */
+ this->pipeline_state.viewport_offset_x = 0;
+ this->pipeline_state.viewport_offset_y = 0;
+ this->pipeline_state.viewport_width = 0;
+ this->pipeline_state.viewport_height = 0;
+ this->pipeline_state.scissor_x = 0;
+ this->pipeline_state.scissor_y = 0;
+ this->pipeline_state.scissor_width = 0;
+ this->pipeline_state.scissor_height = 0;
+ this->pipeline_state.scissor_enabled = false;
+
+ /* Culling State. */
+ this->pipeline_state.culling_enabled = false;
+ this->pipeline_state.cull_mode = GPU_CULL_NONE;
+ this->pipeline_state.front_face = GPU_COUNTERCLOCKWISE;
+
+ /* DATA and IMAGE access state. */
+ this->pipeline_state.unpack_row_length = 0;
+
+ /* Depth State. */
+ this->pipeline_state.depth_stencil_state.depth_write_enable = false;
+ this->pipeline_state.depth_stencil_state.depth_test_enabled = false;
+ this->pipeline_state.depth_stencil_state.depth_range_near = 0.0;
+ this->pipeline_state.depth_stencil_state.depth_range_far = 1.0;
+ this->pipeline_state.depth_stencil_state.depth_function = MTLCompareFunctionAlways;
+ this->pipeline_state.depth_stencil_state.depth_bias = 0.0;
+ this->pipeline_state.depth_stencil_state.depth_slope_scale = 0.0;
+ this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_points = false;
+ this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_lines = false;
+ this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_tris = false;
+
+ /* Stencil State. */
+ this->pipeline_state.depth_stencil_state.stencil_test_enabled = false;
+ this->pipeline_state.depth_stencil_state.stencil_read_mask = 0xFF;
+ this->pipeline_state.depth_stencil_state.stencil_write_mask = 0xFF;
+ this->pipeline_state.depth_stencil_state.stencil_ref = 0;
+ this->pipeline_state.depth_stencil_state.stencil_func = MTLCompareFunctionAlways;
+ this->pipeline_state.depth_stencil_state.stencil_op_front_stencil_fail = MTLStencilOperationKeep;
+ this->pipeline_state.depth_stencil_state.stencil_op_front_depth_fail = MTLStencilOperationKeep;
+ this->pipeline_state.depth_stencil_state.stencil_op_front_depthstencil_pass =
+ MTLStencilOperationKeep;
+ this->pipeline_state.depth_stencil_state.stencil_op_back_stencil_fail = MTLStencilOperationKeep;
+ this->pipeline_state.depth_stencil_state.stencil_op_back_depth_fail = MTLStencilOperationKeep;
+ this->pipeline_state.depth_stencil_state.stencil_op_back_depthstencil_pass =
+ MTLStencilOperationKeep;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Texture State Management
+ * \{ */
+
+void MTLContext::texture_bind(gpu::MTLTexture *mtl_texture, unsigned int texture_unit)
+{
+ BLI_assert(this);
+ BLI_assert(mtl_texture);
+
+ if (texture_unit < 0 || texture_unit >= GPU_max_textures() ||
+ texture_unit >= MTL_MAX_TEXTURE_SLOTS) {
+ MTL_LOG_WARNING("Attempting to bind texture '%s' to invalid texture unit %d\n",
+ mtl_texture->get_name(),
+ texture_unit);
+ BLI_assert(false);
+ return;
+ }
+
+ /* Bind new texture. */
+ this->pipeline_state.texture_bindings[texture_unit].texture_resource = mtl_texture;
+ this->pipeline_state.texture_bindings[texture_unit].used = true;
+ mtl_texture->is_bound_ = true;
+}
+
+void MTLContext::sampler_bind(MTLSamplerState sampler_state, unsigned int sampler_unit)
+{
+ BLI_assert(this);
+ if (sampler_unit < 0 || sampler_unit >= GPU_max_textures() ||
+ sampler_unit >= MTL_MAX_SAMPLER_SLOTS) {
+ MTL_LOG_WARNING("Attempting to bind sampler to invalid sampler unit %d\n", sampler_unit);
+ BLI_assert(false);
+ return;
+ }
+
+ /* Apply binding. */
+ this->pipeline_state.sampler_bindings[sampler_unit] = {true, sampler_state};
+}
+
+void MTLContext::texture_unbind(gpu::MTLTexture *mtl_texture)
+{
+ BLI_assert(mtl_texture);
+
+ /* Iterate through textures in state and unbind. */
+ for (int i = 0; i < min_uu(GPU_max_textures(), MTL_MAX_TEXTURE_SLOTS); i++) {
+ if (this->pipeline_state.texture_bindings[i].texture_resource == mtl_texture) {
+ this->pipeline_state.texture_bindings[i].texture_resource = nullptr;
+ this->pipeline_state.texture_bindings[i].used = false;
+ }
+ }
+
+ /* Locally unbind texture. */
+ mtl_texture->is_bound_ = false;
+}
+
+void MTLContext::texture_unbind_all()
+{
+ /* Iterate through context's bound textures. */
+ for (int t = 0; t < min_uu(GPU_max_textures(), MTL_MAX_TEXTURE_SLOTS); t++) {
+ if (this->pipeline_state.texture_bindings[t].used &&
+ this->pipeline_state.texture_bindings[t].texture_resource) {
+
+ this->pipeline_state.texture_bindings[t].used = false;
+ this->pipeline_state.texture_bindings[t].texture_resource = nullptr;
+ }
+ }
+}
+
+id<MTLSamplerState> MTLContext::get_sampler_from_state(MTLSamplerState sampler_state)
+{
+ BLI_assert((unsigned int)sampler_state >= 0 && ((unsigned int)sampler_state) < GPU_SAMPLER_MAX);
+ return this->sampler_state_cache_[(unsigned int)sampler_state];
+}
+
+id<MTLSamplerState> MTLContext::generate_sampler_from_state(MTLSamplerState sampler_state)
+{
+ /* Check if sampler already exists for given state. */
+ id<MTLSamplerState> st = this->sampler_state_cache_[(unsigned int)sampler_state];
+ if (st != nil) {
+ return st;
+ }
+ else {
+ MTLSamplerDescriptor *descriptor = [[MTLSamplerDescriptor alloc] init];
+ descriptor.normalizedCoordinates = true;
+
+ MTLSamplerAddressMode clamp_type = (sampler_state.state & GPU_SAMPLER_CLAMP_BORDER) ?
+ MTLSamplerAddressModeClampToBorderColor :
+ MTLSamplerAddressModeClampToEdge;
+ descriptor.rAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_R) ?
+ MTLSamplerAddressModeRepeat :
+ clamp_type;
+ descriptor.sAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_S) ?
+ MTLSamplerAddressModeRepeat :
+ clamp_type;
+ descriptor.tAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_T) ?
+ MTLSamplerAddressModeRepeat :
+ clamp_type;
+ descriptor.borderColor = MTLSamplerBorderColorTransparentBlack;
+ descriptor.minFilter = (sampler_state.state & GPU_SAMPLER_FILTER) ?
+ MTLSamplerMinMagFilterLinear :
+ MTLSamplerMinMagFilterNearest;
+ descriptor.magFilter = (sampler_state.state & GPU_SAMPLER_FILTER) ?
+ MTLSamplerMinMagFilterLinear :
+ MTLSamplerMinMagFilterNearest;
+ descriptor.mipFilter = (sampler_state.state & GPU_SAMPLER_MIPMAP) ?
+ MTLSamplerMipFilterLinear :
+ MTLSamplerMipFilterNotMipmapped;
+ descriptor.lodMinClamp = -1000;
+ descriptor.lodMaxClamp = 1000;
+ float aniso_filter = max_ff(16, U.anisotropic_filter);
+ descriptor.maxAnisotropy = (sampler_state.state & GPU_SAMPLER_MIPMAP) ? aniso_filter : 1;
+ descriptor.compareFunction = (sampler_state.state & GPU_SAMPLER_COMPARE) ?
+ MTLCompareFunctionLessEqual :
+ MTLCompareFunctionAlways;
+ descriptor.supportArgumentBuffers = true;
+
+ id<MTLSamplerState> state = [this->device newSamplerStateWithDescriptor:descriptor];
+ this->sampler_state_cache_[(unsigned int)sampler_state] = state;
+
+ BLI_assert(state != nil);
+ [descriptor autorelease];
+ return state;
+ }
+}
+
+id<MTLSamplerState> MTLContext::get_default_sampler_state()
+{
+ if (this->default_sampler_state_ == nil) {
+ this->default_sampler_state_ = this->get_sampler_from_state(DEFAULT_SAMPLER_STATE);
+ }
+ return this->default_sampler_state_;
+}
+
/** \} */
} // blender::gpu
diff --git a/source/blender/gpu/metal/mtl_state.hh b/source/blender/gpu/metal/mtl_state.hh
new file mode 100644
index 00000000000..f2d85f9648b
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_state.hh
@@ -0,0 +1,73 @@
+/** \file
+ * \ingroup gpu
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+
+#include "GPU_state.h"
+#include "gpu_state_private.hh"
+
+namespace blender::gpu {
+
+/* Forward Declarations. */
+class MTLContext;
+
+/**
+ * State manager keeping track of the draw state and applying it before drawing.
+ * Metal Implementation.
+ **/
+class MTLStateManager : public StateManager {
+ public:
+ private:
+ /* Current state of the associated MTLContext.
+ * Avoids resetting the whole state for every change. */
+ GPUState current_;
+ GPUStateMutable current_mutable_;
+ MTLContext *context_;
+
+ public:
+ MTLStateManager(MTLContext *ctx);
+
+ void apply_state(void) override;
+ void force_state(void) override;
+
+ void issue_barrier(eGPUBarrier barrier_bits) override;
+
+ void texture_bind(Texture *tex, eGPUSamplerState sampler, int unit) override;
+ void texture_unbind(Texture *tex) override;
+ void texture_unbind_all(void) override;
+
+ void image_bind(Texture *tex, int unit) override;
+ void image_unbind(Texture *tex) override;
+ void image_unbind_all(void) override;
+
+ void texture_unpack_row_length_set(uint len) override;
+
+ private:
+ void set_write_mask(const eGPUWriteMask value);
+ void set_depth_test(const eGPUDepthTest value);
+ void set_stencil_test(const eGPUStencilTest test, const eGPUStencilOp operation);
+ void set_stencil_mask(const eGPUStencilTest test, const GPUStateMutable state);
+ void set_clip_distances(const int new_dist_len, const int old_dist_len);
+ void set_logic_op(const bool enable);
+ void set_facing(const bool invert);
+ void set_backface_culling(const eGPUFaceCullTest test);
+ void set_provoking_vert(const eGPUProvokingVertex vert);
+ void set_shadow_bias(const bool enable);
+ void set_blend(const eGPUBlend value);
+
+ void set_state(const GPUState &state);
+ void set_mutable_state(const GPUStateMutable &state);
+
+ /* METAL State utility functions. */
+ void mtl_state_init(void);
+ void mtl_depth_range(float near, float far);
+ void mtl_stencil_mask(unsigned int mask);
+ void mtl_stencil_set_func(eGPUStencilTest stencil_func, int ref, unsigned int mask);
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("MTLStateManager")
+};
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_state.mm b/source/blender/gpu/metal/mtl_state.mm
new file mode 100644
index 00000000000..5f52bc55f72
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_state.mm
@@ -0,0 +1,675 @@
+/** \file
+ * \ingroup gpu
+ */
+
+#include "BLI_math_base.h"
+#include "BLI_math_bits.h"
+
+#include "GPU_framebuffer.h"
+
+#include "mtl_context.hh"
+#include "mtl_state.hh"
+
+namespace blender::gpu {
+
+/* -------------------------------------------------------------------- */
+/** \name MTLStateManager
+ * \{ */
+
+void MTLStateManager::mtl_state_init(void)
+{
+ BLI_assert(this->context_);
+ this->context_->pipeline_state_init();
+}
+
+MTLStateManager::MTLStateManager(MTLContext *ctx) : StateManager()
+{
+ /* Initialize State. */
+ this->context_ = ctx;
+ mtl_state_init();
+
+ /* Force update using default state. */
+ current_ = ~state;
+ current_mutable_ = ~mutable_state;
+ set_state(state);
+ set_mutable_state(mutable_state);
+}
+
+void MTLStateManager::apply_state(void)
+{
+ this->set_state(this->state);
+ this->set_mutable_state(this->mutable_state);
+ /* TODO(Metal): Enable after integration of MTLFrameBuffer. */
+ /* static_cast<MTLFrameBuffer *>(this->context_->active_fb)->apply_state(); */
+};
+
+void MTLStateManager::force_state(void)
+{
+ /* Little exception for clip distances since they need to keep the old count correct. */
+ uint32_t clip_distances = current_.clip_distances;
+ current_ = ~this->state;
+ current_.clip_distances = clip_distances;
+ current_mutable_ = ~this->mutable_state;
+ this->set_state(this->state);
+ this->set_mutable_state(this->mutable_state);
+};
+
+void MTLStateManager::set_state(const GPUState &state)
+{
+ GPUState changed = state ^ current_;
+
+ if (changed.blend != 0) {
+ set_blend((eGPUBlend)state.blend);
+ }
+ if (changed.write_mask != 0) {
+ set_write_mask((eGPUWriteMask)state.write_mask);
+ }
+ if (changed.depth_test != 0) {
+ set_depth_test((eGPUDepthTest)state.depth_test);
+ }
+ if (changed.stencil_test != 0 || changed.stencil_op != 0) {
+ set_stencil_test((eGPUStencilTest)state.stencil_test, (eGPUStencilOp)state.stencil_op);
+ set_stencil_mask((eGPUStencilTest)state.stencil_test, mutable_state);
+ }
+ if (changed.clip_distances != 0) {
+ set_clip_distances(state.clip_distances, current_.clip_distances);
+ }
+ if (changed.culling_test != 0) {
+ set_backface_culling((eGPUFaceCullTest)state.culling_test);
+ }
+ if (changed.logic_op_xor != 0) {
+ set_logic_op(state.logic_op_xor);
+ }
+ if (changed.invert_facing != 0) {
+ set_facing(state.invert_facing);
+ }
+ if (changed.provoking_vert != 0) {
+ set_provoking_vert((eGPUProvokingVertex)state.provoking_vert);
+ }
+ if (changed.shadow_bias != 0) {
+ set_shadow_bias(state.shadow_bias);
+ }
+
+ /* TODO remove (Following GLState). */
+ if (changed.polygon_smooth) {
+ /* NOTE: Unsupported in Metal. */
+ }
+ if (changed.line_smooth) {
+ /* NOTE: Unsupported in Metal. */
+ }
+
+ current_ = state;
+}
+
+void MTLStateManager::mtl_depth_range(float near, float far)
+{
+ BLI_assert(this->context_);
+ BLI_assert(near >= 0.0 && near < 1.0);
+ BLI_assert(far > 0.0 && far <= 1.0);
+ MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
+ MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state;
+
+ ds_state.depth_range_near = near;
+ ds_state.depth_range_far = far;
+ pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_VIEWPORT_FLAG;
+}
+
+void MTLStateManager::set_mutable_state(const GPUStateMutable &state)
+{
+ GPUStateMutable changed = state ^ current_mutable_;
+ MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
+
+ if (float_as_uint(changed.point_size) != 0) {
+ pipeline_state.point_size = state.point_size;
+ pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG;
+ }
+
+ if (changed.line_width != 0) {
+ pipeline_state.line_width = state.line_width;
+ pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG;
+ }
+
+ if (changed.depth_range[0] != 0 || changed.depth_range[1] != 0) {
+ /* TODO remove, should modify the projection matrix instead. */
+ mtl_depth_range(state.depth_range[0], state.depth_range[1]);
+ }
+
+ if (changed.stencil_compare_mask != 0 || changed.stencil_reference != 0 ||
+ changed.stencil_write_mask != 0) {
+ set_stencil_mask((eGPUStencilTest)current_.stencil_test, state);
+ }
+
+ current_mutable_ = state;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name State setting functions
+ * \{ */
+
+void MTLStateManager::set_write_mask(const eGPUWriteMask value)
+{
+ BLI_assert(this->context_);
+ MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
+ pipeline_state.depth_stencil_state.depth_write_enable = ((value & GPU_WRITE_DEPTH) != 0);
+ pipeline_state.color_write_mask =
+ (((value & GPU_WRITE_RED) != 0) ? MTLColorWriteMaskRed : MTLColorWriteMaskNone) |
+ (((value & GPU_WRITE_GREEN) != 0) ? MTLColorWriteMaskGreen : MTLColorWriteMaskNone) |
+ (((value & GPU_WRITE_BLUE) != 0) ? MTLColorWriteMaskBlue : MTLColorWriteMaskNone) |
+ (((value & GPU_WRITE_ALPHA) != 0) ? MTLColorWriteMaskAlpha : MTLColorWriteMaskNone);
+ pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG;
+}
+
+static MTLCompareFunction gpu_depth_function_to_metal(eGPUDepthTest depth_func)
+{
+ switch (depth_func) {
+ case GPU_DEPTH_NONE:
+ return MTLCompareFunctionNever;
+ case GPU_DEPTH_LESS:
+ return MTLCompareFunctionLess;
+ case GPU_DEPTH_EQUAL:
+ return MTLCompareFunctionEqual;
+ case GPU_DEPTH_LESS_EQUAL:
+ return MTLCompareFunctionLessEqual;
+ case GPU_DEPTH_GREATER:
+ return MTLCompareFunctionGreater;
+ case GPU_DEPTH_GREATER_EQUAL:
+ return MTLCompareFunctionGreaterEqual;
+ case GPU_DEPTH_ALWAYS:
+ return MTLCompareFunctionAlways;
+ default:
+ BLI_assert(false && "Invalid eGPUDepthTest");
+ break;
+ }
+ return MTLCompareFunctionAlways;
+}
+
+static MTLCompareFunction gpu_stencil_func_to_metal(eGPUStencilTest stencil_func)
+{
+ switch (stencil_func) {
+ case GPU_STENCIL_NONE:
+ return MTLCompareFunctionAlways;
+ case GPU_STENCIL_EQUAL:
+ return MTLCompareFunctionEqual;
+ case GPU_STENCIL_NEQUAL:
+ return MTLCompareFunctionNotEqual;
+ case GPU_STENCIL_ALWAYS:
+ return MTLCompareFunctionAlways;
+ default:
+ BLI_assert(false && "Unrecognised eGPUStencilTest function");
+ break;
+ }
+ return MTLCompareFunctionAlways;
+}
+
+void MTLStateManager::set_depth_test(const eGPUDepthTest value)
+{
+ BLI_assert(this->context_);
+ MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
+ MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state;
+
+ ds_state.depth_test_enabled = (value != GPU_DEPTH_NONE);
+ ds_state.depth_function = gpu_depth_function_to_metal(value);
+ pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG;
+}
+
+void MTLStateManager::mtl_stencil_mask(unsigned int mask)
+{
+ BLI_assert(this->context_);
+ MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
+ pipeline_state.depth_stencil_state.stencil_write_mask = mask;
+ pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG;
+}
+
+void MTLStateManager::mtl_stencil_set_func(eGPUStencilTest stencil_func,
+ int ref,
+ unsigned int mask)
+{
+ BLI_assert(this->context_);
+ MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
+ MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state;
+
+ ds_state.stencil_func = gpu_stencil_func_to_metal(stencil_func);
+ ds_state.stencil_ref = ref;
+ ds_state.stencil_read_mask = mask;
+ pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG;
+}
+
+static void mtl_stencil_set_op_separate(MTLContext *context,
+ eGPUFaceCullTest face,
+ MTLStencilOperation stencil_fail,
+ MTLStencilOperation depth_test_fail,
+ MTLStencilOperation depthstencil_pass)
+{
+ BLI_assert(context);
+ MTLContextGlobalShaderPipelineState &pipeline_state = context->pipeline_state;
+ MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state;
+
+ if (face == GPU_CULL_FRONT) {
+ ds_state.stencil_op_front_stencil_fail = stencil_fail;
+ ds_state.stencil_op_front_depth_fail = depth_test_fail;
+ ds_state.stencil_op_front_depthstencil_pass = depthstencil_pass;
+ }
+ else if (face == GPU_CULL_BACK) {
+ ds_state.stencil_op_back_stencil_fail = stencil_fail;
+ ds_state.stencil_op_back_depth_fail = depth_test_fail;
+ ds_state.stencil_op_back_depthstencil_pass = depthstencil_pass;
+ }
+
+ pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG;
+}
+
+static void mtl_stencil_set_op(MTLContext *context,
+ MTLStencilOperation stencil_fail,
+ MTLStencilOperation depth_test_fail,
+ MTLStencilOperation depthstencil_pass)
+{
+ mtl_stencil_set_op_separate(
+ context, GPU_CULL_FRONT, stencil_fail, depth_test_fail, depthstencil_pass);
+ mtl_stencil_set_op_separate(
+ context, GPU_CULL_BACK, stencil_fail, depth_test_fail, depthstencil_pass);
+}
+
+void MTLStateManager::set_stencil_test(const eGPUStencilTest test, const eGPUStencilOp operation)
+{
+ switch (operation) {
+ case GPU_STENCIL_OP_REPLACE:
+ mtl_stencil_set_op(this->context_,
+ MTLStencilOperationKeep,
+ MTLStencilOperationKeep,
+ MTLStencilOperationReplace);
+ break;
+ case GPU_STENCIL_OP_COUNT_DEPTH_PASS:
+ /* Winding inversed due to flipped Y coordinate system in Metal. */
+ mtl_stencil_set_op_separate(this->context_,
+ GPU_CULL_FRONT,
+ MTLStencilOperationKeep,
+ MTLStencilOperationKeep,
+ MTLStencilOperationIncrementWrap);
+ mtl_stencil_set_op_separate(this->context_,
+ GPU_CULL_BACK,
+ MTLStencilOperationKeep,
+ MTLStencilOperationKeep,
+ MTLStencilOperationDecrementWrap);
+ break;
+ case GPU_STENCIL_OP_COUNT_DEPTH_FAIL:
+ /* Winding inversed due to flipped Y coordinate system in Metal. */
+ mtl_stencil_set_op_separate(this->context_,
+ GPU_CULL_FRONT,
+ MTLStencilOperationKeep,
+ MTLStencilOperationDecrementWrap,
+ MTLStencilOperationKeep);
+ mtl_stencil_set_op_separate(this->context_,
+ GPU_CULL_BACK,
+ MTLStencilOperationKeep,
+ MTLStencilOperationIncrementWrap,
+ MTLStencilOperationKeep);
+ break;
+ case GPU_STENCIL_OP_NONE:
+ default:
+ mtl_stencil_set_op(this->context_,
+ MTLStencilOperationKeep,
+ MTLStencilOperationKeep,
+ MTLStencilOperationKeep);
+ }
+
+ BLI_assert(this->context_);
+ MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
+ pipeline_state.depth_stencil_state.stencil_test_enabled = (test != GPU_STENCIL_NONE);
+ pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG;
+}
+
+void MTLStateManager::set_stencil_mask(const eGPUStencilTest test, const GPUStateMutable state)
+{
+ if (test == GPU_STENCIL_NONE) {
+ mtl_stencil_mask(0x00);
+ mtl_stencil_set_func(GPU_STENCIL_ALWAYS, 0x00, 0x00);
+ }
+ else {
+ mtl_stencil_mask(state.stencil_write_mask);
+ mtl_stencil_set_func(test, state.stencil_reference, state.stencil_compare_mask);
+ }
+}
+
+void MTLStateManager::set_clip_distances(const int new_dist_len, const int old_dist_len)
+{
+ /* TODO(Metal): Support Clip distances in METAL. Clip distance
+ * assignment via shader is supported, but global clip-states require
+ * support. */
+}
+
+void MTLStateManager::set_logic_op(const bool enable)
+{
+ /* NOTE(Metal): Logic Operations not directly supported. */
+}
+
+void MTLStateManager::set_facing(const bool invert)
+{
+ /* Check Current Context. */
+ BLI_assert(this->context_);
+ MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
+
+ /* Apply State -- opposite of GL, as METAL default is GPU_CLOCKWISE, GL default is
+ * COUNTERCLOCKWISE. This needs to be the inverse of the default. */
+ pipeline_state.front_face = (invert) ? GPU_COUNTERCLOCKWISE : GPU_CLOCKWISE;
+
+ /* Mark Dirty - Ensure context updates state between draws. */
+ pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_FRONT_FACING_FLAG;
+ pipeline_state.dirty = true;
+}
+
+void MTLStateManager::set_backface_culling(const eGPUFaceCullTest test)
+{
+ /* Check Current Context. */
+ BLI_assert(this->context_);
+ MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
+
+ /* Apply State. */
+ pipeline_state.culling_enabled = (test != GPU_CULL_NONE);
+ pipeline_state.cull_mode = test;
+
+ /* Mark Dirty - Ensure context updates state between draws. */
+ pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_CULLMODE_FLAG;
+ pipeline_state.dirty = true;
+}
+
+void MTLStateManager::set_provoking_vert(const eGPUProvokingVertex vert)
+{
+ /* NOTE(Metal): Provoking vertex is not a feature in the Metal API.
+ * Shaders are handled on a case-by-case basis using a modified vertex shader.
+ * For example, wireframe rendering and edit-mesh shaders utilize an SSBO-based
+ * vertex fetching mechanism which considers the inverse convention for flat
+ * shading, to ensure consistent results with OpenGL. */
+}
+
+void MTLStateManager::set_shadow_bias(const bool enable)
+{
+ /* Check Current Context. */
+ BLI_assert(this->context_);
+ MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
+ MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state;
+
+ /* Apply State. */
+ if (enable) {
+ ds_state.depth_bias_enabled_for_lines = true;
+ ds_state.depth_bias_enabled_for_tris = true;
+ ds_state.depth_bias = 2.0f;
+ ds_state.depth_slope_scale = 1.0f;
+ }
+ else {
+ ds_state.depth_bias_enabled_for_lines = false;
+ ds_state.depth_bias_enabled_for_tris = false;
+ ds_state.depth_bias = 0.0f;
+ ds_state.depth_slope_scale = 0.0f;
+ }
+
+ /* Mark Dirty - Ensure context updates depth-stencil state between draws. */
+ pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG;
+ pipeline_state.dirty = true;
+}
+
+void MTLStateManager::set_blend(const eGPUBlend value)
+{
+ /**
+ * Factors to the equation.
+ * SRC is fragment shader output.
+ * DST is framebuffer color.
+ * final.rgb = SRC.rgb * src_rgb + DST.rgb * dst_rgb;
+ * final.a = SRC.a * src_alpha + DST.a * dst_alpha;
+ **/
+ MTLBlendFactor src_rgb;
+ MTLBlendFactor dst_rgb;
+ MTLBlendFactor src_alpha;
+ MTLBlendFactor dst_alpha;
+ switch (value) {
+ default:
+ case GPU_BLEND_ALPHA: {
+ src_rgb = MTLBlendFactorSourceAlpha;
+ dst_rgb = MTLBlendFactorOneMinusSourceAlpha;
+ src_alpha = MTLBlendFactorOne;
+ dst_alpha = MTLBlendFactorOneMinusSourceAlpha;
+ break;
+ }
+ case GPU_BLEND_ALPHA_PREMULT: {
+ src_rgb = MTLBlendFactorOne;
+ dst_rgb = MTLBlendFactorOneMinusSourceAlpha;
+ src_alpha = MTLBlendFactorOne;
+ dst_alpha = MTLBlendFactorOneMinusSourceAlpha;
+ break;
+ }
+ case GPU_BLEND_ADDITIVE: {
+ /* Do not let alpha accumulate but pre-multiply the source RGB by it. */
+ src_rgb = MTLBlendFactorSourceAlpha;
+ dst_rgb = MTLBlendFactorOne;
+ src_alpha = MTLBlendFactorZero;
+ dst_alpha = MTLBlendFactorOne;
+ break;
+ }
+ case GPU_BLEND_SUBTRACT:
+ case GPU_BLEND_ADDITIVE_PREMULT: {
+ /* Let alpha accumulate. */
+ src_rgb = MTLBlendFactorOne;
+ dst_rgb = MTLBlendFactorOne;
+ src_alpha = MTLBlendFactorOne;
+ dst_alpha = MTLBlendFactorOne;
+ break;
+ }
+ case GPU_BLEND_MULTIPLY: {
+ src_rgb = MTLBlendFactorDestinationColor;
+ dst_rgb = MTLBlendFactorZero;
+ src_alpha = MTLBlendFactorDestinationAlpha;
+ dst_alpha = MTLBlendFactorZero;
+ break;
+ }
+ case GPU_BLEND_INVERT: {
+ src_rgb = MTLBlendFactorOneMinusDestinationColor;
+ dst_rgb = MTLBlendFactorZero;
+ src_alpha = MTLBlendFactorZero;
+ dst_alpha = MTLBlendFactorOne;
+ break;
+ }
+ case GPU_BLEND_OIT: {
+ src_rgb = MTLBlendFactorOne;
+ dst_rgb = MTLBlendFactorOne;
+ src_alpha = MTLBlendFactorZero;
+ dst_alpha = MTLBlendFactorOneMinusSourceAlpha;
+ break;
+ }
+ case GPU_BLEND_BACKGROUND: {
+ src_rgb = MTLBlendFactorOneMinusDestinationAlpha;
+ dst_rgb = MTLBlendFactorSourceAlpha;
+ src_alpha = MTLBlendFactorZero;
+ dst_alpha = MTLBlendFactorSourceAlpha;
+ break;
+ }
+ case GPU_BLEND_ALPHA_UNDER_PREMUL: {
+ src_rgb = MTLBlendFactorOneMinusDestinationAlpha;
+ dst_rgb = MTLBlendFactorOne;
+ src_alpha = MTLBlendFactorOneMinusDestinationAlpha;
+ dst_alpha = MTLBlendFactorOne;
+ break;
+ }
+ case GPU_BLEND_CUSTOM: {
+ src_rgb = MTLBlendFactorOne;
+ dst_rgb = MTLBlendFactorSource1Color;
+ src_alpha = MTLBlendFactorOne;
+ dst_alpha = MTLBlendFactorSource1Alpha;
+ break;
+ }
+ }
+
+ /* Check Current Context. */
+ BLI_assert(this->context_);
+ MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
+
+ if (value == GPU_BLEND_SUBTRACT) {
+ pipeline_state.rgb_blend_op = MTLBlendOperationReverseSubtract;
+ pipeline_state.alpha_blend_op = MTLBlendOperationReverseSubtract;
+ }
+ else {
+ pipeline_state.rgb_blend_op = MTLBlendOperationAdd;
+ pipeline_state.alpha_blend_op = MTLBlendOperationAdd;
+ }
+
+ /* Apply State. */
+ pipeline_state.blending_enabled = (value != GPU_BLEND_NONE);
+ pipeline_state.src_rgb_blend_factor = src_rgb;
+ pipeline_state.dest_rgb_blend_factor = dst_rgb;
+ pipeline_state.src_alpha_blend_factor = src_alpha;
+ pipeline_state.dest_alpha_blend_factor = dst_alpha;
+
+ /* Mark Dirty - Ensure context updates PSOs between draws. */
+ pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG;
+ pipeline_state.dirty = true;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Memory barrier
+ * \{ */
+
+/* NOTE(Metal): Granular option for specifying before/after stages for a barrier
+ * Would be a useful feature. */
+/*void MTLStateManager::issue_barrier(eGPUBarrier barrier_bits,
+ eGPUStageBarrierBits before_stages,
+ eGPUStageBarrierBits after_stages) */
+void MTLStateManager::issue_barrier(eGPUBarrier barrier_bits)
+{
+ /* NOTE(Metal): The Metal API implicitly tracks dependencies between resources.
+ * Memory barriers and execution barriers (Fences/Events) can be used to coordinate
+ * this explicitly, however, in most cases, the driver will be able to
+ * resolve these dependencies automatically.
+ * For untracked resources, such as MTLHeap's, explicit barriers are necessary. */
+ eGPUStageBarrierBits before_stages = GPU_BARRIER_STAGE_ANY;
+ eGPUStageBarrierBits after_stages = GPU_BARRIER_STAGE_ANY;
+
+ MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get());
+ BLI_assert(ctx);
+ if (ctx->is_render_pass_active()) {
+
+ /* Apple Silicon does not support memory barriers.
+ * We do not currently need these due to implicit API guarantees.
+ * NOTE(Metal): MTLFence/MTLEvent may be required to synchronize work if
+ * untracked resources are ever used. */
+ if ([ctx->device hasUnifiedMemory]) {
+ return;
+ }
+
+ /* Issue barrier. */
+ /* TODO(Metal): To be completed pending implementation of RenderCommandEncoder management. */
+ id<MTLRenderCommandEncoder> rec = nil; // ctx->get_active_render_command_encoder();
+ BLI_assert(rec);
+
+ /* Only supporting Metal on 10.15 onward anyway - Check required for warnings. */
+ if (@available(macOS 10.14, *)) {
+ MTLBarrierScope scope = 0;
+ if (barrier_bits & GPU_BARRIER_SHADER_IMAGE_ACCESS ||
+ barrier_bits & GPU_BARRIER_TEXTURE_FETCH) {
+ scope = scope | MTLBarrierScopeTextures | MTLBarrierScopeRenderTargets;
+ }
+ if (barrier_bits & GPU_BARRIER_SHADER_STORAGE ||
+ barrier_bits & GPU_BARRIER_VERTEX_ATTRIB_ARRAY ||
+ barrier_bits & GPU_BARRIER_ELEMENT_ARRAY) {
+ scope = scope | MTLBarrierScopeBuffers;
+ }
+
+ MTLRenderStages before_stage_flags = 0;
+ MTLRenderStages after_stage_flags = 0;
+ if (before_stages & GPU_BARRIER_STAGE_VERTEX &&
+ !(before_stages & GPU_BARRIER_STAGE_FRAGMENT)) {
+ before_stage_flags = before_stage_flags | MTLRenderStageVertex;
+ }
+ if (before_stages & GPU_BARRIER_STAGE_FRAGMENT) {
+ before_stage_flags = before_stage_flags | MTLRenderStageFragment;
+ }
+ if (after_stages & GPU_BARRIER_STAGE_VERTEX) {
+ after_stage_flags = after_stage_flags | MTLRenderStageVertex;
+ }
+ if (after_stages & GPU_BARRIER_STAGE_FRAGMENT) {
+ after_stage_flags = MTLRenderStageFragment;
+ }
+
+ if (scope != 0) {
+ [rec memoryBarrierWithScope:scope
+ afterStages:after_stage_flags
+ beforeStages:before_stage_flags];
+ }
+ }
+ }
+}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Texture State Management
+ * \{ */
+
+void MTLStateManager::texture_unpack_row_length_set(uint len)
+{
+ /* Set source image row data stride when uploading image data to the GPU. */
+ MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
+ ctx->pipeline_state.unpack_row_length = len;
+}
+
+void MTLStateManager::texture_bind(Texture *tex_, eGPUSamplerState sampler_type, int unit)
+{
+ BLI_assert(tex_);
+ gpu::MTLTexture *mtl_tex = static_cast<gpu::MTLTexture *>(tex_);
+ BLI_assert(mtl_tex);
+
+ MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
+ if (unit >= 0) {
+ ctx->texture_bind(mtl_tex, unit);
+
+ /* Fetching textures default sampler configuration and applying
+ * eGPUSampler State on top. This path exists to support
+ * Any of the sampler state which is associated with the
+ * texture itself such as min/max mip levels. */
+ MTLSamplerState sampler = mtl_tex->get_sampler_state();
+ sampler.state = sampler_type;
+
+ ctx->sampler_bind(sampler, unit);
+ }
+}
+
+void MTLStateManager::texture_unbind(Texture *tex_)
+{
+ BLI_assert(tex_);
+ gpu::MTLTexture *mtl_tex = static_cast<gpu::MTLTexture *>(tex_);
+ BLI_assert(mtl_tex);
+ MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
+ ctx->texture_unbind(mtl_tex);
+}
+
+void MTLStateManager::texture_unbind_all(void)
+{
+ MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
+ BLI_assert(ctx);
+ ctx->texture_unbind_all();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Image Binding (from image load store)
+ * \{ */
+
+void MTLStateManager::image_bind(Texture *tex_, int unit)
+{
+ this->texture_bind(tex_, GPU_SAMPLER_DEFAULT, unit);
+}
+
+void MTLStateManager::image_unbind(Texture *tex_)
+{
+ this->texture_unbind(tex_);
+}
+
+void MTLStateManager::image_unbind_all(void)
+{
+ this->texture_unbind_all();
+}
+
+/** \} */
+
+} // blender::gpu
diff --git a/source/blender/gpu/metal/mtl_texture.hh b/source/blender/gpu/metal/mtl_texture.hh
index e013bb5321a..b820256ec36 100644
--- a/source/blender/gpu/metal/mtl_texture.hh
+++ b/source/blender/gpu/metal/mtl_texture.hh
@@ -47,16 +47,13 @@ struct TextureUpdateRoutineSpecialisation {
(component_count_input == other.component_count_input) &&
(component_count_output == other.component_count_output));
}
-};
-template<> struct blender::DefaultHash<TextureUpdateRoutineSpecialisation> {
- inline uint64_t operator()(const TextureUpdateRoutineSpecialisation &key) const
+ inline uint64_t hash() const
{
-
- DefaultHash<std::string> string_hasher;
+ blender::DefaultHash<std::string> string_hasher;
return (uint64_t)string_hasher(
- key.input_data_type + key.output_data_type +
- std::to_string((key.component_count_input << 8) + key.component_count_output));
+ this->input_data_type + this->output_data_type +
+ std::to_string((this->component_count_input << 8) + this->component_count_output));
}
};
@@ -78,12 +75,10 @@ struct DepthTextureUpdateRoutineSpecialisation {
{
return ((data_mode == other.data_mode));
}
-};
-template<> struct blender::DefaultHash<DepthTextureUpdateRoutineSpecialisation> {
- inline uint64_t operator()(const DepthTextureUpdateRoutineSpecialisation &key) const
+ inline uint64_t hash() const
{
- return (uint64_t)(key.data_mode);
+ return (uint64_t)(this->data_mode);
}
};
@@ -109,17 +104,14 @@ struct TextureReadRoutineSpecialisation {
(component_count_output == other.component_count_output) &&
(depth_format_mode == other.depth_format_mode));
}
-};
-template<> struct blender::DefaultHash<TextureReadRoutineSpecialisation> {
- inline uint64_t operator()(const TextureReadRoutineSpecialisation &key) const
+ inline uint64_t hash() const
{
-
- DefaultHash<std::string> string_hasher;
- return (uint64_t)string_hasher(key.input_data_type + key.output_data_type +
- std::to_string((key.component_count_input << 8) +
- key.component_count_output +
- (key.depth_format_mode << 28)));
+ blender::DefaultHash<std::string> string_hasher;
+ return (uint64_t)string_hasher(this->input_data_type + this->output_data_type +
+ std::to_string((this->component_count_input << 8) +
+ this->component_count_output +
+ (this->depth_format_mode << 28)));
}
};
@@ -158,21 +150,6 @@ typedef struct MTLSamplerState {
const MTLSamplerState DEFAULT_SAMPLER_STATE = {GPU_SAMPLER_DEFAULT /*, 0, 9999*/};
-} // namespace blender::gpu
-
-template<> struct blender::DefaultHash<blender::gpu::MTLSamplerState> {
- inline uint64_t operator()(const blender::gpu::MTLSamplerState &key) const
- {
- const DefaultHash<unsigned int> uint_hasher;
- uint64_t main_hash = (uint64_t)uint_hasher((unsigned int)(key.state));
-
- /* Hash other parameters as needed. */
- return main_hash;
- }
-};
-
-namespace blender::gpu {
-
class MTLTexture : public Texture {
friend class MTLContext;
friend class MTLStateManager;
@@ -242,7 +219,7 @@ class MTLTexture : public Texture {
id<MTLBuffer> vert_buffer_mtl_;
int vert_buffer_offset_;
- /* Core parameters and subresources. */
+ /* Core parameters and sub-resources. */
eGPUTextureUsage gpu_image_usage_flags_;
/* Whether the texture's properties or state has changed (e.g. mipmap range), and re-baking of
diff --git a/source/blender/gpu/metal/mtl_texture.mm b/source/blender/gpu/metal/mtl_texture.mm
index df3efdd12e7..ca19d1f9e4b 100644
--- a/source/blender/gpu/metal/mtl_texture.mm
+++ b/source/blender/gpu/metal/mtl_texture.mm
@@ -563,7 +563,7 @@ void gpu::MTLTexture::update_sub(
return;
}
- /* Check Format writeability. */
+ /* Check Format write-ability. */
if (mtl_format_get_writeable_view_format(destination_format) == MTLPixelFormatInvalid) {
MTL_LOG_ERROR(
"[Error]: Updating texture -- destination MTLPixelFormat '%d' does not support write "
@@ -1163,7 +1163,7 @@ void gpu::MTLTexture::mip_range_set(int min, int max)
{
BLI_assert(min <= max && min >= 0 && max <= mipmaps_);
- /* Note:
+ /* NOTE:
* - mip_min_ and mip_max_ are used to Clamp LODs during sampling.
* - Given functions like Framebuffer::recursive_downsample modifies the mip range
* between each layer, we do not want to be re-baking the texture.
@@ -1769,8 +1769,8 @@ void gpu::MTLTexture::ensure_baked()
/* CUBE TEXTURES */
case GPU_TEXTURE_CUBE:
case GPU_TEXTURE_CUBE_ARRAY: {
- /* Note: For a cubemap 'Texture::d_' refers to total number of faces, not just array slices
- */
+ /* NOTE: For a cube-map 'Texture::d_' refers to total number of faces,
+ * not just array slices. */
BLI_assert(this->w_ > 0 && this->h_ > 0);
this->texture_descriptor_ = [[MTLTextureDescriptor alloc] init];
this->texture_descriptor_.pixelFormat = mtl_format;
diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh
index 2dde8d6c86b..e5b879f1f15 100644
--- a/source/blender/gpu/opengl/gl_texture.hh
+++ b/source/blender/gpu/opengl/gl_texture.hh
@@ -363,7 +363,7 @@ inline GLenum to_gl_data_format(eGPUTextureFormat format)
}
/**
- * Assume Unorm / Float target. Used with #glReadPixels.
+ * Assume UNORM/Float target. Used with #glReadPixels.
*/
inline GLenum channel_len_to_gl(int channel_len)
{
diff --git a/source/blender/gpu/opengl/gl_vertex_array.cc b/source/blender/gpu/opengl/gl_vertex_array.cc
index 04f60f10d41..cfcf77fe705 100644
--- a/source/blender/gpu/opengl/gl_vertex_array.cc
+++ b/source/blender/gpu/opengl/gl_vertex_array.cc
@@ -39,8 +39,8 @@ static uint16_t vbo_bind(const ShaderInterface *interface,
const GPUVertAttr *a = &format->attrs[a_idx];
if (format->deinterleaved) {
- offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].sz) * v_len;
- stride = a->sz;
+ offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].size) * v_len;
+ stride = a->size;
}
else {
offset = a->offset;
diff --git a/source/blender/gpu/opengl/gl_vertex_buffer.cc b/source/blender/gpu/opengl/gl_vertex_buffer.cc
index a7a0c92431f..6942a220892 100644
--- a/source/blender/gpu/opengl/gl_vertex_buffer.cc
+++ b/source/blender/gpu/opengl/gl_vertex_buffer.cc
@@ -5,6 +5,8 @@
* \ingroup gpu
*/
+#include "GPU_texture.h"
+
#include "gl_context.hh"
#include "gl_vertex_buffer.hh"
@@ -38,6 +40,7 @@ void GLVertBuf::release_data()
}
if (vbo_id_ != 0) {
+ GPU_TEXTURE_FREE_SAFE(buffer_texture_);
GLContext::buf_free(vbo_id_);
vbo_id_ = 0;
memory_usage -= vbo_size_;
@@ -51,6 +54,7 @@ void GLVertBuf::duplicate_data(VertBuf *dst_)
BLI_assert(GLContext::get() != nullptr);
GLVertBuf *src = this;
GLVertBuf *dst = static_cast<GLVertBuf *>(dst_);
+ dst->buffer_texture_ = nullptr;
if (src->vbo_id_ != 0) {
dst->vbo_size_ = src->size_used_get();
@@ -111,6 +115,16 @@ void GLVertBuf::bind_as_ssbo(uint binding)
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, vbo_id_);
}
+void GLVertBuf::bind_as_texture(uint binding)
+{
+ bind();
+ BLI_assert(vbo_id_ != 0);
+ if (buffer_texture_ == nullptr) {
+ buffer_texture_ = GPU_texture_create_from_vertbuf("vertbuf_as_texture", wrap(this));
+ }
+ GPU_texture_bind(buffer_texture_, binding);
+}
+
const void *GLVertBuf::read() const
{
BLI_assert(is_active());
diff --git a/source/blender/gpu/opengl/gl_vertex_buffer.hh b/source/blender/gpu/opengl/gl_vertex_buffer.hh
index 4c29c17dcf7..88d2a455778 100644
--- a/source/blender/gpu/opengl/gl_vertex_buffer.hh
+++ b/source/blender/gpu/opengl/gl_vertex_buffer.hh
@@ -11,6 +11,8 @@
#include "glew-mx.h"
+#include "GPU_texture.h"
+
#include "gpu_vertex_buffer_private.hh"
namespace blender {
@@ -23,6 +25,8 @@ class GLVertBuf : public VertBuf {
private:
/** OpenGL buffer handle. Init on first upload. Immutable after that. */
GLuint vbo_id_ = 0;
+ /** Texture used if the buffer is bound as buffer texture. Init on first use. */
+ struct ::GPUTexture *buffer_texture_ = nullptr;
/** Defines whether the buffer handle is wrapped by this GLVertBuf, i.e. we do not own it and
* should not free it. */
bool is_wrapper_ = false;
@@ -46,6 +50,7 @@ class GLVertBuf : public VertBuf {
void upload_data() override;
void duplicate_data(VertBuf *dst) override;
void bind_as_ssbo(uint binding) override;
+ void bind_as_texture(uint binding) override;
private:
bool is_active() const;
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_color_ramp.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_color_ramp.glsl
index 17240da4050..17240da4050 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_color_ramp.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_color_ramp.glsl
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_color_util.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl
index a5c3a990d90..fe89985ae7f 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_color_util.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl
@@ -90,6 +90,56 @@ void hsv_to_rgb(vec4 hsv, out vec4 outcol)
outcol = vec4(rgb, hsv.w);
}
+void rgb_to_hsl(vec4 rgb, out vec4 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.0, (cmax + cmin) / 2.0);
+
+ if (cmax == cmin) {
+ h = s = 0.0; /* achromatic */
+ }
+ else {
+ float cdelta = cmax - cmin;
+ s = l > 0.5 ? cdelta / (2.0 - cmax - cmin) : cdelta / (cmax + cmin);
+ if (cmax == rgb[0]) {
+ h = (rgb[1] - rgb[2]) / cdelta + (rgb[1] < rgb[2] ? 6.0 : 0.0);
+ }
+ else if (cmax == rgb[1]) {
+ h = (rgb[2] - rgb[0]) / cdelta + 2.0;
+ }
+ else {
+ h = (rgb[0] - rgb[1]) / cdelta + 4.0;
+ }
+ }
+ h /= 6.0;
+
+ outcol = vec4(h, s, l, rgb.w);
+}
+
+void hsl_to_rgb(vec4 hsl, out vec4 outcol)
+{
+ float nr, ng, nb, chroma, h, s, l;
+
+ h = hsl[0];
+ s = hsl[1];
+ l = hsl[2];
+
+ nr = abs(h * 6.0 - 3.0) - 1.0;
+ ng = 2.0 - abs(h * 6.0 - 2.0);
+ nb = 2.0 - abs(h * 6.0 - 4.0);
+
+ nr = clamp(nr, 0.0, 1.0);
+ nb = clamp(nb, 0.0, 1.0);
+ ng = clamp(ng, 0.0, 1.0);
+
+ chroma = (1.0 - abs(2.0 * l - 1.0)) * s;
+
+ outcol = vec4((nr - 0.5) * chroma + l, (ng - 0.5) * chroma + l, (nb - 0.5) * chroma + l, hsl.w);
+}
+
void color_alpha_clear(vec4 color, out vec4 result)
{
result = vec4(color.rgb, 1.0);
diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl
new file mode 100644
index 00000000000..8948ed77557
--- /dev/null
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl
@@ -0,0 +1,162 @@
+vec4 white_balance(vec4 color, vec4 black_level, vec4 white_level)
+{
+ vec4 range = max(white_level - black_level, vec4(1e-5f));
+ return (color - black_level) / range;
+}
+
+float extrapolate_if_needed(float parameter, float value, float start_slope, float end_slope)
+{
+ if (parameter < 0.0) {
+ return value + parameter * start_slope;
+ }
+
+ if (parameter > 1.0) {
+ return value + (parameter - 1.0) * end_slope;
+ }
+
+ return value;
+}
+
+/* Same as extrapolate_if_needed but vectorized. */
+vec3 extrapolate_if_needed(vec3 parameters, vec3 values, vec3 start_slopes, vec3 end_slopes)
+{
+ vec3 end_or_zero_slopes = mix(vec3(0.0), end_slopes, greaterThan(parameters, vec3(1.0)));
+ vec3 slopes = mix(end_or_zero_slopes, start_slopes, lessThan(parameters, vec3(0.0)));
+ parameters = parameters - mix(vec3(0.0), vec3(1.0), greaterThan(parameters, vec3(1.0)));
+ return values + parameters * slopes;
+}
+
+/* Curve maps are stored in sampler objects that are evaluated in the [0, 1] range, so normalize
+ * parameters accordingly. */
+#define NORMALIZE_PARAMETER(parameter, minimum, range) ((parameter - minimum) * range)
+
+void curves_combined_rgb(float factor,
+ vec4 color,
+ vec4 black_level,
+ vec4 white_level,
+ sampler1DArray curve_map,
+ const float layer,
+ vec4 range_minimums,
+ vec4 range_dividers,
+ vec4 start_slopes,
+ vec4 end_slopes,
+ out vec4 result)
+{
+ vec4 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. */
+ vec3 parameters = NORMALIZE_PARAMETER(balanced.rgb, range_minimums.aaa, range_dividers.aaa);
+ result.r = texture(curve_map, vec2(parameters.x, layer)).a;
+ result.g = texture(curve_map, vec2(parameters.y, layer)).a;
+ result.b = texture(curve_map, vec2(parameters.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. */
+ parameters = NORMALIZE_PARAMETER(result.rgb, range_minimums.rgb, range_dividers.rgb);
+ result.r = texture(curve_map, vec2(parameters.r, layer)).r;
+ result.g = texture(curve_map, vec2(parameters.g, layer)).g;
+ result.b = texture(curve_map, vec2(parameters.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);
+}
+
+void curves_combined_only(float factor,
+ vec4 color,
+ vec4 black_level,
+ vec4 white_level,
+ sampler1DArray curve_map,
+ const float layer,
+ float range_minimum,
+ float range_divider,
+ float start_slope,
+ float end_slope,
+ out vec4 result)
+{
+ vec4 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. */
+ vec3 parameters = NORMALIZE_PARAMETER(balanced.rgb, range_minimum, range_divider);
+ result.r = texture(curve_map, vec2(parameters.x, layer)).a;
+ result.g = texture(curve_map, vec2(parameters.y, layer)).a;
+ result.b = texture(curve_map, vec2(parameters.z, layer)).a;
+
+ /* Then, extrapolate if needed. */
+ result.rgb = extrapolate_if_needed(parameters, result.rgb, vec3(start_slope), vec3(end_slope));
+ result.a = color.a;
+
+ result = mix(color, result, factor);
+}
+
+void curves_vector(vec3 vector,
+ sampler1DArray curve_map,
+ const float layer,
+ vec3 range_minimums,
+ vec3 range_dividers,
+ vec3 start_slopes,
+ vec3 end_slopes,
+ out vec3 result)
+{
+ /* Evaluate each component on its curve map. */
+ vec3 parameters = NORMALIZE_PARAMETER(vector, range_minimums, range_dividers);
+ result.x = texture(curve_map, vec2(parameters.x, layer)).x;
+ result.y = texture(curve_map, vec2(parameters.y, layer)).y;
+ result.z = texture(curve_map, vec2(parameters.z, layer)).z;
+
+ /* Then, extrapolate if needed. */
+ result = extrapolate_if_needed(parameters, result, start_slopes, end_slopes);
+}
+
+void curves_vector_mixed(float factor,
+ vec3 vector,
+ sampler1DArray curve_map,
+ const float layer,
+ vec3 range_minimums,
+ vec3 range_dividers,
+ vec3 start_slopes,
+ vec3 end_slopes,
+ out vec3 result)
+{
+ curves_vector(
+ vector, curve_map, layer, range_minimums, range_dividers, start_slopes, end_slopes, result);
+ result = mix(vector, result, factor);
+}
+
+void curves_float(float value,
+ sampler1DArray curve_map,
+ const float layer,
+ float range_minimum,
+ float range_divider,
+ float start_slope,
+ float end_slope,
+ out float result)
+{
+ /* Evaluate the normalized value on the first curve map. */
+ float parameter = NORMALIZE_PARAMETER(value, range_minimum, range_divider);
+ result = texture(curve_map, vec2(parameter, layer)).x;
+
+ /* Then, extrapolate if needed. */
+ result = extrapolate_if_needed(parameter, result, start_slope, end_slope);
+}
+
+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,
+ out float result)
+{
+ curves_float(
+ value, curve_map, layer, range_minimum, range_divider, start_slope, end_slope, result);
+ result = mix(value, result, factor);
+}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_hash.glsl
index 32d61f06a65..32d61f06a65 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_hash.glsl
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_math.glsl
index 0948ce2c9fa..5f640f64056 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_math.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
void math_add(float a, float b, float c, out float result)
{
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl
index 91a8996939a..124654963fd 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl
@@ -139,75 +139,3 @@ mat3 euler_to_mat3(vec3 euler)
mat[2][2] = cy * cx;
return mat;
}
-
-void normal_transform_object_to_world(vec3 vin, out vec3 vout)
-{
- vout = normal_object_to_world(vin);
-}
-
-void normal_transform_world_to_object(vec3 vin, out vec3 vout)
-{
- vout = normal_world_to_object(vin);
-}
-
-void direction_transform_object_to_world(vec3 vin, out vec3 vout)
-{
- vout = transform_direction(ModelMatrix, vin);
-}
-
-void direction_transform_object_to_view(vec3 vin, out vec3 vout)
-{
- vout = transform_direction(ModelMatrix, vin);
- vout = transform_direction(ViewMatrix, vout);
-}
-
-void direction_transform_view_to_world(vec3 vin, out vec3 vout)
-{
- vout = transform_direction(ViewMatrixInverse, vin);
-}
-
-void direction_transform_view_to_object(vec3 vin, out vec3 vout)
-{
- vout = transform_direction(ViewMatrixInverse, vin);
- vout = transform_direction(ModelMatrixInverse, vout);
-}
-
-void direction_transform_world_to_view(vec3 vin, out vec3 vout)
-{
- vout = transform_direction(ViewMatrix, vin);
-}
-
-void direction_transform_world_to_object(vec3 vin, out vec3 vout)
-{
- vout = transform_direction(ModelMatrixInverse, vin);
-}
-
-void point_transform_object_to_world(vec3 vin, out vec3 vout)
-{
- vout = point_object_to_world(vin);
-}
-
-void point_transform_object_to_view(vec3 vin, out vec3 vout)
-{
- vout = point_object_to_view(vin);
-}
-
-void point_transform_view_to_world(vec3 vin, out vec3 vout)
-{
- vout = point_view_to_world(vin);
-}
-
-void point_transform_view_to_object(vec3 vin, out vec3 vout)
-{
- vout = point_view_to_object(vin);
-}
-
-void point_transform_world_to_view(vec3 vin, out vec3 vout)
-{
- vout = point_world_to_view(vin);
-}
-
-void point_transform_world_to_object(vec3 vin, out vec3 vout)
-{
- vout = point_world_to_object(vin);
-}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl
index 157a6a27c15..f9652f1150b 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
void mix_blend(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
diff --git a/source/blender/gpu/shaders/gpu_shader_geometry.glsl b/source/blender/gpu/shaders/gpu_shader_geometry.glsl
deleted file mode 100644
index e0e899cfb35..00000000000
--- a/source/blender/gpu/shaders/gpu_shader_geometry.glsl
+++ /dev/null
@@ -1,93 +0,0 @@
-
-#define INTERP_FACE_VARYING_2(result, fvarOffset, tessCoord) \
- { \
- vec2 v[4]; \
- int primOffset = (gl_PrimitiveID + PrimitiveIdBase) * 4; \
- for (int i = 0; i < 4; i++) { \
- int index = (primOffset + i) * osd_fvar_count + fvarOffset; \
- v[i] = vec2(texelFetch(FVarDataBuffer, index).s, texelFetch(FVarDataBuffer, index + 1).s); \
- } \
- result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); \
- }
-
-#define INTERP_FACE_VARYING_ATT_2(result, fvarOffset, tessCoord) \
- { \
- vec2 tmp; \
- INTERP_FACE_VARYING_2(tmp, fvarOffset, tessCoord); \
- result = vec3(tmp, 0); \
- }
-
-out block
-{
- VertexData v;
-}
-outpt;
-
-void set_mtface_vertex_attrs(vec2 st);
-
-void emit_flat(int index, vec3 normal)
-{
- outpt.v.position = inpt[index].v.position;
- outpt.v.normal = normal;
-
- /* Compatibility */
- varnormal = outpt.v.normal;
- varposition = outpt.v.position.xyz;
-
- /* TODO(sergey): Only uniform subdivisions atm. */
- vec2 quadst[4] = vec2[](vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1));
- vec2 st = quadst[index];
-
- INTERP_FACE_VARYING_2(outpt.v.uv, osd_active_uv_offset, st);
-
- set_mtface_vertex_attrs(st);
-
- gl_Position = ProjectionMatrix * inpt[index].v.position;
- EmitVertex();
-}
-
-void emit_smooth(int index)
-{
- outpt.v.position = inpt[index].v.position;
- outpt.v.normal = inpt[index].v.normal;
-
- /* Compatibility */
- varnormal = outpt.v.normal;
- varposition = outpt.v.position.xyz;
-
- /* TODO(sergey): Only uniform subdivisions atm. */
- vec2 quadst[4] = vec2[](vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1));
- vec2 st = quadst[index];
-
- INTERP_FACE_VARYING_2(outpt.v.uv, osd_active_uv_offset, st);
-
- set_mtface_vertex_attrs(st);
-
- gl_Position = ProjectionMatrix * inpt[index].v.position;
- EmitVertex();
-}
-
-void main()
-{
- gl_PrimitiveID = gl_PrimitiveIDIn;
-
- if (osd_flat_shading) {
- vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
- vec3 B = (inpt[3].v.position - inpt[1].v.position).xyz;
- vec3 flat_normal = normalize(cross(B, A));
- emit_flat(0, flat_normal);
- emit_flat(1, flat_normal);
- emit_flat(3, flat_normal);
- emit_flat(2, flat_normal);
- }
- else {
- emit_smooth(0);
- emit_smooth(1);
- emit_smooth(3);
- emit_smooth(2);
- }
- EndPrimitive();
-}
-
-void set_mtface_vertex_attrs(vec2 st)
-{
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh
new file mode 100644
index 00000000000..94cf58933af
--- /dev/null
+++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "gpu_interface_info.hh"
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(gpu_shader_3D_image)
+ .vertex_in(0, Type::VEC3, "pos")
+ .vertex_in(1, Type::VEC2, "texCoord")
+ .vertex_out(smooth_tex_coord_interp_iface)
+ .fragment_out(0, Type::VEC4, "fragColor")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
+ .sampler(0, ImageType::FLOAT_2D, "image")
+ .vertex_source("gpu_shader_3D_image_vert.glsl")
+ .fragment_source("gpu_shader_image_frag.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh
index 369fe3ac293..ae7edeb16e2 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh
@@ -28,7 +28,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_clipped_uniform_color)
.fragment_out(0, Type::VEC4, "fragColor")
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.push_constant(Type::VEC4, "color")
- /* TODO(fclem): Put thoses two to one UBO. */
+ /* TODO(@fclem): Put those two to one UBO. */
.push_constant(Type::MAT4, "ModelMatrix")
.push_constant(Type::VEC4, "ClipPlane")
.vertex_source("gpu_shader_3D_clipped_uniform_color_vert.glsl")
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_combine_color.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_combine_color.glsl
new file mode 100644
index 00000000000..e68d0d98484
--- /dev/null
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_combine_color.glsl
@@ -0,0 +1,16 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+void combine_color_rgb(float r, float g, float b, out vec4 col)
+{
+ col = vec4(r, g, b, 1.0);
+}
+
+void combine_color_hsv(float h, float s, float v, out vec4 col)
+{
+ hsv_to_rgb(vec4(h, s, v, 1.0), col);
+}
+
+void combine_color_hsl(float h, float s, float l, out vec4 col)
+{
+ hsl_to_rgb(vec4(h, s, l, 1.0), col);
+}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl
index e8f444080b9..4d9e16afe66 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
void combine_hsv(float h, float s, float v, out vec4 col)
{
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl
deleted file mode 100644
index 514409f7fdf..00000000000
--- a/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl
+++ /dev/null
@@ -1,33 +0,0 @@
-/* ext is vec4(in_x, in_dy, out_x, out_dy). */
-float curve_float_extrapolate(float x, float y, vec4 ext)
-{
- if (x < 0.0) {
- return y + x * ext.y;
- }
- else if (x > 1.0) {
- return y + (x - 1.0) * ext.w;
- }
- else {
- return y;
- }
-}
-
-#define RANGE_RESCALE(x, min, range) ((x - min) * range)
-
-void curve_float(float fac,
- float vec,
- sampler1DArray curvemap,
- float layer,
- float range,
- vec4 ext,
- out float outvec)
-{
- float xyz_min = ext.x;
- vec = RANGE_RESCALE(vec, xyz_min, range);
-
- outvec = texture(curvemap, vec2(vec, layer)).x;
-
- outvec = curve_float_extrapolate(vec, outvec, ext);
-
- outvec = mix(vec, outvec, fac);
-}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl
index 7f502f74c0c..6d52e97cca1 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl)
/* The fractal_noise functions are all exactly the same except for the input type. */
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl
index 29fb09ceebd..64681cd795a 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
void node_gamma(vec4 col, float gamma, out vec4 outcol)
{
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
index 2d5114c3bad..61458b05c86 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl)
void node_hair_info(float hair_length,
out float is_strand,
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl
index 30b808508e9..5223828e176 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
void hue_sat(float hue, float sat, float value, float fac, vec4 col, out vec4 outcol)
{
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl
index 119ee3c0eaa..a81e6d36a55 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
float smootherstep(float edge0, float edge1, float x)
{
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl
index 312c57231c5..b59257506c9 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
void mapping_mat4(
vec3 vec, vec4 m0, vec4 m1, vec4 m2, vec4 m3, vec3 minvec, vec3 maxvec, out vec3 outvec)
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl
index c84f34a834c..881e38ea11a 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl)
/* clang-format off */
#define FLOORFRAC(x, x_int, x_fract) { float x_floor = floor(x); x_int = int(x_floor); x_fract = x - x_floor; }
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl
index ad3d4737193..251103ae57c 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl)
void node_point_info(out vec3 position, out float radius, out float random)
{
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_rgb_curves.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_rgb_curves.glsl
deleted file mode 100644
index 054fdddf7c3..00000000000
--- a/source/blender/gpu/shaders/material/gpu_shader_material_rgb_curves.glsl
+++ /dev/null
@@ -1,73 +0,0 @@
-/* ext is vec4(in_x, in_dy, out_x, out_dy). */
-float curve_extrapolate(float x, float y, vec4 ext)
-{
- if (x < 0.0) {
- return y + x * ext.y;
- }
- else if (x > 1.0) {
- return y + (x - 1.0) * ext.w;
- }
- else {
- return y;
- }
-}
-
-#define RANGE_RESCALE(x, min, range) ((x - min) * range)
-
-void curves_rgb(float fac,
- vec4 col,
- sampler1DArray curvemap,
- float layer,
- vec4 range,
- vec4 ext_r,
- vec4 ext_g,
- vec4 ext_b,
- vec4 ext_a,
- out vec4 outcol)
-{
- vec4 co = vec4(RANGE_RESCALE(col.rgb, ext_a.x, range.a), layer);
- vec3 samp;
- samp.r = texture(curvemap, co.xw).a;
- samp.g = texture(curvemap, co.yw).a;
- samp.b = texture(curvemap, co.zw).a;
-
- samp.r = curve_extrapolate(co.x, samp.r, ext_a);
- samp.g = curve_extrapolate(co.y, samp.g, ext_a);
- samp.b = curve_extrapolate(co.z, samp.b, ext_a);
-
- vec3 rgb_min = vec3(ext_r.x, ext_g.x, ext_b.x);
- co.xyz = RANGE_RESCALE(samp.rgb, rgb_min, range.rgb);
-
- samp.r = texture(curvemap, co.xw).r;
- samp.g = texture(curvemap, co.yw).g;
- samp.b = texture(curvemap, co.zw).b;
-
- outcol.r = curve_extrapolate(co.x, samp.r, ext_r);
- outcol.g = curve_extrapolate(co.y, samp.g, ext_g);
- outcol.b = curve_extrapolate(co.z, samp.b, ext_b);
- outcol.a = col.a;
-
- outcol = mix(col, outcol, fac);
-}
-
-void curves_rgb_opti(float fac,
- vec4 col,
- sampler1DArray curvemap,
- float layer,
- vec4 range,
- vec4 ext_a,
- out vec4 outcol)
-{
- vec4 co = vec4(RANGE_RESCALE(col.rgb, ext_a.x, range.a), layer);
- vec3 samp;
- samp.r = texture(curvemap, co.xw).a;
- samp.g = texture(curvemap, co.yw).a;
- samp.b = texture(curvemap, co.zw).a;
-
- outcol.r = curve_extrapolate(co.x, samp.r, ext_a);
- outcol.g = curve_extrapolate(co.y, samp.g, ext_a);
- outcol.b = curve_extrapolate(co.z, samp.b, ext_a);
- outcol.a = col.a;
-
- outcol = mix(col, outcol, fac);
-}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_separate_color.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_separate_color.glsl
new file mode 100644
index 00000000000..2dd51029cef
--- /dev/null
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_separate_color.glsl
@@ -0,0 +1,28 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+void separate_color_rgb(vec4 col, out float r, out float g, out float b)
+{
+ r = col.r;
+ g = col.g;
+ b = col.b;
+}
+
+void separate_color_hsv(vec4 col, out float r, out float g, out float b)
+{
+ vec4 hsv;
+
+ rgb_to_hsv(col, hsv);
+ r = hsv[0];
+ g = hsv[1];
+ b = hsv[2];
+}
+
+void separate_color_hsl(vec4 col, out float r, out float g, out float b)
+{
+ vec4 hsl;
+
+ rgb_to_hsl(col, hsl);
+ r = hsl[0];
+ g = hsl[1];
+ b = hsl[2];
+}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl
index 180e0fd1940..8e475ec39a7 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
void separate_hsv(vec4 col, out float h, out float s, out float v)
{
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl
index edc2fa32177..8d9773913ff 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl
@@ -1,5 +1,5 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl)
-#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
vec2 calc_brick_texture(vec3 p,
float mortar_size,
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl
index da131978f72..cf7d6ae18e6 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
void node_tex_environment_equirectangular(vec3 co, out vec3 uv)
{
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl
index 1552a2facc3..961fe23e67e 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl)
/* 1D Musgrave fBm
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl
index c90b2211dcf..3df6f7b29fb 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_material_fractal_noise.glsl)
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl
index dd12b602edf..0fb8ef15f5f 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl
@@ -1,5 +1,5 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
-#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
/*
* Original code is under the MIT License, Copyright (c) 2013 Inigo Quilez.
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl
index eed98232d0b..c24a9417219 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_material_fractal_noise.glsl)
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl
index 030b58f0736..c5081372aa4 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl)
/* White Noise */
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_transform_utils.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_transform_utils.glsl
new file mode 100644
index 00000000000..87048e5c5d6
--- /dev/null
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_transform_utils.glsl
@@ -0,0 +1,71 @@
+void normal_transform_object_to_world(vec3 vin, out vec3 vout)
+{
+ vout = normal_object_to_world(vin);
+}
+
+void normal_transform_world_to_object(vec3 vin, out vec3 vout)
+{
+ vout = normal_world_to_object(vin);
+}
+
+void direction_transform_object_to_world(vec3 vin, out vec3 vout)
+{
+ vout = transform_direction(ModelMatrix, vin);
+}
+
+void direction_transform_object_to_view(vec3 vin, out vec3 vout)
+{
+ vout = transform_direction(ModelMatrix, vin);
+ vout = transform_direction(ViewMatrix, vout);
+}
+
+void direction_transform_view_to_world(vec3 vin, out vec3 vout)
+{
+ vout = transform_direction(ViewMatrixInverse, vin);
+}
+
+void direction_transform_view_to_object(vec3 vin, out vec3 vout)
+{
+ vout = transform_direction(ViewMatrixInverse, vin);
+ vout = transform_direction(ModelMatrixInverse, vout);
+}
+
+void direction_transform_world_to_view(vec3 vin, out vec3 vout)
+{
+ vout = transform_direction(ViewMatrix, vin);
+}
+
+void direction_transform_world_to_object(vec3 vin, out vec3 vout)
+{
+ vout = transform_direction(ModelMatrixInverse, vin);
+}
+
+void point_transform_object_to_world(vec3 vin, out vec3 vout)
+{
+ vout = point_object_to_world(vin);
+}
+
+void point_transform_object_to_view(vec3 vin, out vec3 vout)
+{
+ vout = point_object_to_view(vin);
+}
+
+void point_transform_view_to_world(vec3 vin, out vec3 vout)
+{
+ vout = point_view_to_world(vin);
+}
+
+void point_transform_view_to_object(vec3 vin, out vec3 vout)
+{
+ vout = point_view_to_object(vin);
+}
+
+void point_transform_world_to_view(vec3 vin, out vec3 vout)
+{
+ vout = point_world_to_view(vin);
+}
+
+void point_transform_world_to_object(vec3 vin, out vec3 vout)
+{
+ vout = point_world_to_object(vin);
+}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl
deleted file mode 100644
index f6dec1b24e2..00000000000
--- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl
+++ /dev/null
@@ -1,41 +0,0 @@
-/* ext is vec4(in_x, in_dy, out_x, out_dy). */
-float curve_vec_extrapolate(float x, float y, vec4 ext)
-{
- if (x < 0.0) {
- return y + x * ext.y;
- }
- else if (x > 1.0) {
- return y + (x - 1.0) * ext.w;
- }
- else {
- return y;
- }
-}
-
-#define RANGE_RESCALE(x, min, range) ((x - min) * range)
-
-void curves_vec(float fac,
- vec3 vec,
- sampler1DArray curvemap,
- float layer,
- vec3 range,
- vec4 ext_x,
- vec4 ext_y,
- vec4 ext_z,
- out vec3 outvec)
-{
- vec4 co = vec4(vec, layer);
-
- vec3 xyz_min = vec3(ext_x.x, ext_y.x, ext_z.x);
- co.xyz = RANGE_RESCALE(co.xyz, xyz_min, range);
-
- outvec.x = texture(curvemap, co.xw).x;
- outvec.y = texture(curvemap, co.yw).y;
- outvec.z = texture(curvemap, co.zw).z;
-
- outvec.x = curve_vec_extrapolate(co.x, outvec.r, ext_x);
- outvec.y = curve_vec_extrapolate(co.y, outvec.g, ext_y);
- outvec.z = curve_vec_extrapolate(co.z, outvec.b, ext_z);
-
- outvec = mix(vec, outvec, fac);
-}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl
index 8f6bf17f195..018784c42a5 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
void vector_math_add(vec3 a, vec3 b, vec3 c, float scale, out vec3 outVector, out float outValue)
{
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl
index ff0fb1c0418..8f7bd26ca18 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
vec3 rotate_around_axis(vec3 p, vec3 axis, float angle)
{
diff --git a/source/blender/gpu/tests/gpu_shader_builtin_test.cc b/source/blender/gpu/tests/gpu_shader_builtin_test.cc
index 6ef8a032a73..5dc70a8bf0f 100644
--- a/source/blender/gpu/tests/gpu_shader_builtin_test.cc
+++ b/source/blender/gpu/tests/gpu_shader_builtin_test.cc
@@ -35,6 +35,7 @@ static void test_shader_builtin()
test_compile_builtin_shader(GPU_SHADER_2D_UNIFORM_COLOR, GPU_SHADER_CFG_DEFAULT);
test_compile_builtin_shader(GPU_SHADER_2D_FLAT_COLOR, GPU_SHADER_CFG_DEFAULT);
test_compile_builtin_shader(GPU_SHADER_2D_SMOOTH_COLOR, GPU_SHADER_CFG_DEFAULT);
+ test_compile_builtin_shader(GPU_SHADER_3D_IMAGE, GPU_SHADER_CFG_DEFAULT);
test_compile_builtin_shader(GPU_SHADER_2D_IMAGE, GPU_SHADER_CFG_DEFAULT);
test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_COLOR, GPU_SHADER_CFG_DEFAULT);
test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_DESATURATE_COLOR, GPU_SHADER_CFG_DEFAULT);