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:
authorOmar Emara <mail@OmarEmara.dev>2022-05-20 14:11:03 +0300
committerOmar Emara <mail@OmarEmara.dev>2022-05-20 14:11:03 +0300
commit30132dec018a669eeb28aba072991581c8ca61bd (patch)
tree73ccdfa51e8a7a5b660fe5416f6feecc78a5ea87 /source/blender/gpu
parent48006f8b5f72b233986f83bbd20a4370c6afd89d (diff)
parenta89f829f12f56214b0e463b33f24edf27228db1f (diff)
Merge branch 'master' into temp-viewport-compositor-merge
Diffstat (limited to 'source/blender/gpu')
-rw-r--r--source/blender/gpu/CMakeLists.txt6
-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_state.h13
-rw-r--r--source/blender/gpu/GPU_storage_buffer.h12
-rw-r--r--source/blender/gpu/GPU_vertex_buffer.h1
-rw-r--r--source/blender/gpu/intern/gpu_material.c2
-rw-r--r--source/blender/gpu/intern/gpu_shader_dependency.cc12
-rw-r--r--source/blender/gpu/intern/gpu_storage_buffer.cc7
-rw-r--r--source/blender/gpu/intern/gpu_storage_buffer_private.hh1
-rw-r--r--source/blender/gpu/intern/gpu_vertex_buffer.cc5
-rw-r--r--source/blender/gpu/intern/gpu_vertex_buffer_private.hh1
-rw-r--r--source/blender/gpu/metal/kernels/depth_2d_update_float_frag.glsl2
-rw-r--r--source/blender/gpu/metal/kernels/depth_2d_update_int24_frag.glsl2
-rw-r--r--source/blender/gpu/metal/kernels/depth_2d_update_int32_frag.glsl2
-rw-r--r--source/blender/gpu/metal/kernels/depth_2d_update_vert.glsl2
-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.hh47
-rw-r--r--source/blender/gpu/metal/mtl_texture.mm2
-rw-r--r--source/blender/gpu/opengl/gl_backend.cc8
-rw-r--r--source/blender/gpu/opengl/gl_debug.cc20
-rw-r--r--source/blender/gpu/opengl/gl_shader.cc4
-rw-r--r--source/blender/gpu/opengl/gl_storage_buffer.cc24
-rw-r--r--source/blender/gpu/opengl/gl_storage_buffer.hh1
-rw-r--r--source/blender/gpu/opengl/gl_vertex_buffer.cc14
-rw-r--r--source/blender/gpu/opengl/gl_vertex_buffer.hh10
-rw-r--r--source/blender/gpu/shaders/gpu_shader_geometry.glsl93
30 files changed, 1435 insertions, 166 deletions
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index a2db89f849e..bf2f52dfa7a 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,8 +304,6 @@ 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
@@ -483,6 +484,7 @@ list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR})
set(SRC_SHADER_CREATE_INFOS
../draw/engines/basic/shaders/infos/basic_depth_info.hh
../draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
+ ../draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh
../draw/engines/gpencil/shaders/infos/gpencil_info.hh
../draw/engines/gpencil/shaders/infos/gpencil_vfx_info.hh
../draw/engines/overlay/shaders/infos/antialiasing_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..8c91d60812f
--- /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
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_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_storage_buffer.h b/source/blender/gpu/GPU_storage_buffer.h
index 1478d490e23..ca6a848786b 100644
--- a/source/blender/gpu/GPU_storage_buffer.h
+++ b/source/blender/gpu/GPU_storage_buffer.h
@@ -47,6 +47,18 @@ void GPU_storagebuf_clear(GPUStorageBuf *ssbo,
void *data);
void GPU_storagebuf_clear_to_zero(GPUStorageBuf *ssbo);
+/**
+ * \brief Copy a part of a vertex buffer to a storage buffer.
+ *
+ * \param ssbo: destination storage buffer
+ * \param src: source vertex buffer
+ * \param dst_offset: where to start copying to (in bytes).
+ * \param src_offset: where to start copying from (in bytes).
+ * \param copy_size: byte size of the segment to copy.
+ */
+void GPU_storagebuf_copy_sub_from_vertbuf(
+ GPUStorageBuf *ssbo, GPUVertBuf *src, uint dst_offset, uint src_offset, uint copy_size);
+
#ifdef __cplusplus
}
#endif
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/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c
index e7337073773..964a1a51361 100644
--- a/source/blender/gpu/intern/gpu_material.c
+++ b/source/blender/gpu/intern/gpu_material.c
@@ -643,7 +643,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_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc
index ac9f13a3343..8d53a170d22 100644
--- a/source/blender/gpu/intern/gpu_shader_dependency.cc
+++ b/source/blender/gpu/intern/gpu_shader_dependency.cc
@@ -695,14 +695,20 @@ Vector<const char *> gpu_shader_dependency_get_resolved_source(
const StringRefNull shader_source_name)
{
Vector<const char *> result;
- GPUSource *source = g_sources->lookup(shader_source_name);
- source->build(result);
+ GPUSource *src = g_sources->lookup_default(shader_source_name, nullptr);
+ if (src == nullptr) {
+ std::cout << "Error source not found : " << shader_source_name << std::endl;
+ }
+ src->build(result);
return result;
}
StringRefNull gpu_shader_dependency_get_source(const StringRefNull shader_source_name)
{
- GPUSource *src = g_sources->lookup(shader_source_name);
+ GPUSource *src = g_sources->lookup_default(shader_source_name, nullptr);
+ if (src == nullptr) {
+ std::cout << "Error source not found : " << shader_source_name << std::endl;
+ }
return src->source;
}
diff --git a/source/blender/gpu/intern/gpu_storage_buffer.cc b/source/blender/gpu/intern/gpu_storage_buffer.cc
index 806acad90fe..68020ec66f4 100644
--- a/source/blender/gpu/intern/gpu_storage_buffer.cc
+++ b/source/blender/gpu/intern/gpu_storage_buffer.cc
@@ -19,6 +19,7 @@
#include "GPU_storage_buffer.h"
#include "gpu_storage_buffer_private.hh"
+#include "gpu_vertex_buffer_private.hh"
/* -------------------------------------------------------------------- */
/** \name Creation & Deletion
@@ -103,4 +104,10 @@ void GPU_storagebuf_clear_to_zero(GPUStorageBuf *ssbo)
GPU_storagebuf_clear(ssbo, GPU_R32UI, GPU_DATA_UINT, &data);
}
+void GPU_storagebuf_copy_sub_from_vertbuf(
+ GPUStorageBuf *ssbo, GPUVertBuf *src, uint dst_offset, uint src_offset, uint copy_size)
+{
+ unwrap(ssbo)->copy_sub(unwrap(src), dst_offset, src_offset, copy_size);
+}
+
/** \} */
diff --git a/source/blender/gpu/intern/gpu_storage_buffer_private.hh b/source/blender/gpu/intern/gpu_storage_buffer_private.hh
index 06e7c2c0e9d..091e6c2d386 100644
--- a/source/blender/gpu/intern/gpu_storage_buffer_private.hh
+++ b/source/blender/gpu/intern/gpu_storage_buffer_private.hh
@@ -43,6 +43,7 @@ class StorageBuf {
virtual void clear(eGPUTextureFormat internal_format,
eGPUDataFormat data_format,
void *data) = 0;
+ virtual void copy_sub(VertBuf *src, uint dst_offset, uint src_offset, uint copy_size) = 0;
};
/* Syntactic sugar. */
diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.cc b/source/blender/gpu/intern/gpu_vertex_buffer.cc
index 13f409cfba5..f47970d48d1 100644
--- a/source/blender/gpu/intern/gpu_vertex_buffer.cc
+++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc
@@ -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/metal/kernels/depth_2d_update_float_frag.glsl b/source/blender/gpu/metal/kernels/depth_2d_update_float_frag.glsl
index ecb2dddcd63..9fd54f3f31f 100644
--- a/source/blender/gpu/metal/kernels/depth_2d_update_float_frag.glsl
+++ b/source/blender/gpu/metal/kernels/depth_2d_update_float_frag.glsl
@@ -7,4 +7,4 @@ in vec2 texCoord_interp;
void main()
{
gl_FragDepth = textureLod(source_data, texCoord_interp, mip).r;
-} \ No newline at end of file
+}
diff --git a/source/blender/gpu/metal/kernels/depth_2d_update_int24_frag.glsl b/source/blender/gpu/metal/kernels/depth_2d_update_int24_frag.glsl
index 99661a760f0..7483343503f 100644
--- a/source/blender/gpu/metal/kernels/depth_2d_update_int24_frag.glsl
+++ b/source/blender/gpu/metal/kernels/depth_2d_update_int24_frag.glsl
@@ -10,4 +10,4 @@ void main()
uint stencil = (val >> 24) & 0xFFu;
uint depth = (val)&0xFFFFFFu;
gl_FragDepth = float(depth) / float(0xFFFFFFu);
-} \ No newline at end of file
+}
diff --git a/source/blender/gpu/metal/kernels/depth_2d_update_int32_frag.glsl b/source/blender/gpu/metal/kernels/depth_2d_update_int32_frag.glsl
index 15271ab2cdd..75d42c57f73 100644
--- a/source/blender/gpu/metal/kernels/depth_2d_update_int32_frag.glsl
+++ b/source/blender/gpu/metal/kernels/depth_2d_update_int32_frag.glsl
@@ -9,4 +9,4 @@ void main()
uint val = textureLod(source_data, texCoord_interp, mip).r;
uint depth = (val) & (0xFFFFFFFFu);
gl_FragDepth = float(depth) / float(0xFFFFFFFFu);
-} \ No newline at end of file
+}
diff --git a/source/blender/gpu/metal/kernels/depth_2d_update_vert.glsl b/source/blender/gpu/metal/kernels/depth_2d_update_vert.glsl
index 092ae45b719..faae68d2f55 100644
--- a/source/blender/gpu/metal/kernels/depth_2d_update_vert.glsl
+++ b/source/blender/gpu/metal/kernels/depth_2d_update_vert.glsl
@@ -30,4 +30,4 @@ void main()
texCoord_interp = tex.zy;
}
gl_Position = vec4(rect.xy, 0.0f, 1.0f);
-} \ No newline at end of file
+}
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 47f03a5777b..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;
diff --git a/source/blender/gpu/metal/mtl_texture.mm b/source/blender/gpu/metal/mtl_texture.mm
index 7ec3d390416..ca19d1f9e4b 100644
--- a/source/blender/gpu/metal/mtl_texture.mm
+++ b/source/blender/gpu/metal/mtl_texture.mm
@@ -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.
diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc
index 17d9b392c69..1cd2301aa4e 100644
--- a/source/blender/gpu/opengl/gl_backend.cc
+++ b/source/blender/gpu/opengl/gl_backend.cc
@@ -419,10 +419,10 @@ static void detect_workarounds()
GCaps.shader_storage_buffer_objects_support = false;
}
- /* Certain Intel based platforms don't clear the viewport textures. Always clearing leads to
- * noticeable performance regressions. */
- if (GPU_type_matches(
- GPU_DEVICE_INTEL, static_cast<eGPUOSType>(GPU_OS_MAC | GPU_OS_UNIX), GPU_DRIVER_ANY)) {
+ /* Certain Intel/AMD based platforms don't clear the viewport textures. Always clearing leads to
+ * noticeable performance regressions on other platforms as well. */
+ if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY) ||
+ GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_UNIX, GPU_DRIVER_ANY)) {
GCaps.clear_viewport_workaround = true;
}
diff --git a/source/blender/gpu/opengl/gl_debug.cc b/source/blender/gpu/opengl/gl_debug.cc
index a3288ff4cff..f82138e0d65 100644
--- a/source/blender/gpu/opengl/gl_debug.cc
+++ b/source/blender/gpu/opengl/gl_debug.cc
@@ -331,11 +331,21 @@ void object_label(GLenum type, GLuint object, const char *name)
char label[64];
SNPRINTF(label, "%s%s%s", to_str_prefix(type), name, to_str_suffix(type));
/* Small convenience for caller. */
- if (ELEM(type, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER, GL_VERTEX_SHADER, GL_COMPUTE_SHADER)) {
- type = GL_SHADER;
- }
- if (ELEM(type, GL_UNIFORM_BUFFER)) {
- type = GL_BUFFER;
+ switch (type) {
+ case GL_FRAGMENT_SHADER:
+ case GL_GEOMETRY_SHADER:
+ case GL_VERTEX_SHADER:
+ case GL_COMPUTE_SHADER:
+ type = GL_SHADER;
+ break;
+ case GL_UNIFORM_BUFFER:
+ case GL_SHADER_STORAGE_BUFFER:
+ case GL_ARRAY_BUFFER:
+ case GL_ELEMENT_ARRAY_BUFFER:
+ type = GL_BUFFER;
+ break;
+ default:
+ break;
}
glObjectLabel(type, object, -1, label);
}
diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc
index 957bc8b24be..5a28b8b7318 100644
--- a/source/blender/gpu/opengl/gl_shader.cc
+++ b/source/blender/gpu/opengl/gl_shader.cc
@@ -876,7 +876,7 @@ static char *glsl_patch_default_get()
static char *glsl_patch_compute_get()
{
/** Used for shader patching. Init once. */
- static char patch[512] = "\0";
+ static char patch[2048] = "\0";
if (patch[0] != '\0') {
return patch;
}
@@ -889,6 +889,8 @@ static char *glsl_patch_compute_get()
/* Array compat. */
STR_CONCAT(patch, slen, "#define gpu_Array(_type) _type[]\n");
+ STR_CONCAT(patch, slen, datatoc_glsl_shader_defines_glsl);
+
BLI_assert(slen < sizeof(patch));
return patch;
}
diff --git a/source/blender/gpu/opengl/gl_storage_buffer.cc b/source/blender/gpu/opengl/gl_storage_buffer.cc
index 109bb65fcb7..b30674fe5fa 100644
--- a/source/blender/gpu/opengl/gl_storage_buffer.cc
+++ b/source/blender/gpu/opengl/gl_storage_buffer.cc
@@ -144,6 +144,30 @@ void GLStorageBuf::clear(eGPUTextureFormat internal_format, eGPUDataFormat data_
}
}
+void GLStorageBuf::copy_sub(VertBuf *src_, uint dst_offset, uint src_offset, uint copy_size)
+{
+ GLVertBuf *src = static_cast<GLVertBuf *>(src_);
+ GLStorageBuf *dst = this;
+
+ if (dst->ssbo_id_ == 0) {
+ dst->init();
+ }
+ if (src->vbo_id_ == 0) {
+ src->bind();
+ }
+
+ if (GLContext::direct_state_access_support) {
+ glCopyNamedBufferSubData(src->vbo_id_, dst->ssbo_id_, src_offset, dst_offset, copy_size);
+ }
+ else {
+ /* This binds the buffer to GL_ARRAY_BUFFER and upload the data if any. */
+ src->bind();
+ glBindBuffer(GL_COPY_WRITE_BUFFER, dst->ssbo_id_);
+ glCopyBufferSubData(GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, src_offset, dst_offset, copy_size);
+ glBindBuffer(GL_COPY_WRITE_BUFFER, 0);
+ }
+}
+
/** \} */
} // namespace blender::gpu
diff --git a/source/blender/gpu/opengl/gl_storage_buffer.hh b/source/blender/gpu/opengl/gl_storage_buffer.hh
index c808a0bdda1..96052fe0065 100644
--- a/source/blender/gpu/opengl/gl_storage_buffer.hh
+++ b/source/blender/gpu/opengl/gl_storage_buffer.hh
@@ -36,6 +36,7 @@ class GLStorageBuf : public StorageBuf {
void bind(int slot) override;
void unbind() override;
void clear(eGPUTextureFormat internal_format, eGPUDataFormat data_format, void *data) override;
+ void copy_sub(VertBuf *src, uint dst_offset, uint src_offset, uint copy_size) override;
/* Special internal function to bind SSBOs to indirect argument targets. */
void bind_as(GLenum target);
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..e0a21587b60 100644
--- a/source/blender/gpu/opengl/gl_vertex_buffer.hh
+++ b/source/blender/gpu/opengl/gl_vertex_buffer.hh
@@ -11,18 +11,23 @@
#include "glew-mx.h"
+#include "GPU_texture.h"
+
#include "gpu_vertex_buffer_private.hh"
namespace blender {
namespace gpu {
class GLVertBuf : public VertBuf {
- friend class GLTexture; /* For buffer texture. */
- friend class GLShader; /* For transform feedback. */
+ friend class GLTexture; /* For buffer texture. */
+ friend class GLShader; /* For transform feedback. */
+ friend class GLStorageBuf; /* For sub copy. */
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 +51,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/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)
-{