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.txt44
-rw-r--r--source/blender/gpu/GPU_context.h2
-rw-r--r--source/blender/gpu/GPU_framebuffer.h3
-rw-r--r--source/blender/gpu/GPU_platform.h1
-rw-r--r--source/blender/gpu/GPU_shader.h4
-rw-r--r--source/blender/gpu/intern/gpu_context.cc26
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer.cc5
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer_private.hh10
-rw-r--r--source/blender/gpu/intern/gpu_immediate.cc11
-rw-r--r--source/blender/gpu/intern/gpu_immediate_private.hh8
-rw-r--r--source/blender/gpu/intern/gpu_shader_builder.cc49
-rw-r--r--source/blender/gpu/intern/gpu_shader_builder_stubs.cc9
-rw-r--r--source/blender/gpu/intern/gpu_shader_builtin.c5
-rw-r--r--source/blender/gpu/intern/gpu_texture_private.hh9
-rw-r--r--source/blender/gpu/intern/gpu_vertex_format.cc8
-rw-r--r--source/blender/gpu/intern/gpu_viewport.c4
-rw-r--r--source/blender/gpu/metal/mtl_backend.mm3
-rw-r--r--source/blender/gpu/metal/mtl_batch.hh115
-rw-r--r--source/blender/gpu/metal/mtl_batch.mm998
-rw-r--r--source/blender/gpu/metal/mtl_context.mm10
-rw-r--r--source/blender/gpu/metal/mtl_drawlist.hh58
-rw-r--r--source/blender/gpu/metal/mtl_drawlist.mm284
-rw-r--r--source/blender/gpu/metal/mtl_immediate.mm3
-rw-r--r--source/blender/gpu/metal/mtl_pso_descriptor_state.hh13
-rw-r--r--source/blender/gpu/metal/mtl_shader_generator.hh2
-rw-r--r--source/blender/gpu/metal/mtl_shader_interface.mm10
-rw-r--r--source/blender/gpu/metal/mtl_texture.hh4
-rw-r--r--source/blender/gpu/metal/mtl_texture.mm97
-rw-r--r--source/blender/gpu/metal/mtl_texture_util.mm2
-rw-r--r--source/blender/gpu/opengl/gl_batch.cc4
-rw-r--r--source/blender/gpu/opengl/gl_shader_interface.cc10
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_blur.glsl21
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_blur_variable_size.glsl21
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_normalize.glsl10
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_tone_map_photoreceptor.glsl22
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_tone_map_simple.glsl26
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_normalize_info.hh12
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_parallel_reduction_info.hh81
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_tone_map_photoreceptor_info.hh16
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_tone_map_simple_info.hh13
-rw-r--r--source/blender/gpu/shaders/gpu_shader_icon_frag.glsl42
-rw-r--r--source/blender/gpu/shaders/gpu_shader_icon_vert.glsl37
-rw-r--r--source/blender/gpu/shaders/infos/gpu_interface_info.hh3
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_icon_info.hh22
-rw-r--r--source/blender/gpu/vulkan/vk_backend.cc107
-rw-r--r--source/blender/gpu/vulkan/vk_backend.hh42
-rw-r--r--source/blender/gpu/vulkan/vk_batch.cc27
-rw-r--r--source/blender/gpu/vulkan/vk_batch.hh24
-rw-r--r--source/blender/gpu/vulkan/vk_context.cc48
-rw-r--r--source/blender/gpu/vulkan/vk_context.hh34
-rw-r--r--source/blender/gpu/vulkan/vk_drawlist.cc20
-rw-r--r--source/blender/gpu/vulkan/vk_drawlist.hh20
-rw-r--r--source/blender/gpu/vulkan/vk_framebuffer.cc62
-rw-r--r--source/blender/gpu/vulkan/vk_framebuffer.hh50
-rw-r--r--source/blender/gpu/vulkan/vk_index_buffer.cc33
-rw-r--r--source/blender/gpu/vulkan/vk_index_buffer.hh28
-rw-r--r--source/blender/gpu/vulkan/vk_query.cc28
-rw-r--r--source/blender/gpu/vulkan/vk_query.hh22
-rw-r--r--source/blender/gpu/vulkan/vk_shader.cc102
-rw-r--r--source/blender/gpu/vulkan/vk_shader.hh48
-rw-r--r--source/blender/gpu/vulkan/vk_storage_buffer.cc42
-rw-r--r--source/blender/gpu/vulkan/vk_storage_buffer.hh30
-rw-r--r--source/blender/gpu/vulkan/vk_texture.cc70
-rw-r--r--source/blender/gpu/vulkan/vk_texture.hh39
-rw-r--r--source/blender/gpu/vulkan/vk_uniform_buffer.cc24
-rw-r--r--source/blender/gpu/vulkan/vk_uniform_buffer.hh25
-rw-r--r--source/blender/gpu/vulkan/vk_vertex_buffer.cc58
-rw-r--r--source/blender/gpu/vulkan/vk_vertex_buffer.hh32
68 files changed, 3046 insertions, 106 deletions
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index f387a4588b6..bfbbf1be225 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -12,6 +12,7 @@ endif()
set(INC
.
intern
+ vulkan
metal
opengl
../blenkernel
@@ -184,11 +185,41 @@ set(OPENGL_SRC
opengl/gl_vertex_buffer.hh
)
+set(VULKAN_SRC
+ vulkan/vk_backend.cc
+ vulkan/vk_batch.cc
+ vulkan/vk_context.cc
+ vulkan/vk_drawlist.cc
+ vulkan/vk_framebuffer.cc
+ vulkan/vk_index_buffer.cc
+ vulkan/vk_query.cc
+ vulkan/vk_shader.cc
+ vulkan/vk_storage_buffer.cc
+ vulkan/vk_texture.cc
+ vulkan/vk_uniform_buffer.cc
+ vulkan/vk_vertex_buffer.cc
+
+ vulkan/vk_backend.hh
+ vulkan/vk_batch.hh
+ vulkan/vk_context.hh
+ vulkan/vk_drawlist.hh
+ vulkan/vk_framebuffer.hh
+ vulkan/vk_index_buffer.hh
+ vulkan/vk_query.hh
+ vulkan/vk_shader.hh
+ vulkan/vk_storage_buffer.hh
+ vulkan/vk_texture.hh
+ vulkan/vk_uniform_buffer.hh
+ vulkan/vk_vertex_buffer.hh
+)
+
set(METAL_SRC
metal/mtl_backend.mm
+ metal/mtl_batch.mm
metal/mtl_command_buffer.mm
metal/mtl_context.mm
metal/mtl_debug.mm
+ metal/mtl_drawlist.mm
metal/mtl_framebuffer.mm
metal/mtl_immediate.mm
metal/mtl_index_buffer.mm
@@ -233,6 +264,10 @@ if(WITH_OPENGL)
list(APPEND SRC ${OPENGL_SRC})
endif()
+if(WITH_VULKAN_BACKEND)
+ list(APPEND SRC ${VULKAN_SRC})
+endif()
+
if(WITH_METAL_BACKEND)
list(APPEND SRC ${METAL_SRC})
endif()
@@ -280,6 +315,8 @@ set(GLSL_SRC
shaders/gpu_shader_2D_image_vert.glsl
shaders/gpu_shader_2D_image_rect_vert.glsl
shaders/gpu_shader_2D_image_multi_rect_vert.glsl
+ shaders/gpu_shader_icon_frag.glsl
+ shaders/gpu_shader_icon_vert.glsl
shaders/gpu_shader_image_frag.glsl
shaders/gpu_shader_image_desaturate_frag.glsl
shaders/gpu_shader_image_overlays_merge_frag.glsl
@@ -347,6 +384,7 @@ set(GLSL_SRC
shaders/compositor/compositor_morphological_distance_feather.glsl
shaders/compositor/compositor_morphological_distance_threshold.glsl
shaders/compositor/compositor_morphological_step.glsl
+ shaders/compositor/compositor_normalize.glsl
shaders/compositor/compositor_parallel_reduction.glsl
shaders/compositor/compositor_projector_lens_distortion.glsl
shaders/compositor/compositor_realize_on_domain.glsl
@@ -355,6 +393,8 @@ set(GLSL_SRC
shaders/compositor/compositor_split_viewer.glsl
shaders/compositor/compositor_symmetric_blur.glsl
shaders/compositor/compositor_symmetric_separable_blur.glsl
+ shaders/compositor/compositor_tone_map_photoreceptor.glsl
+ shaders/compositor/compositor_tone_map_simple.glsl
shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl
shaders/compositor/library/gpu_shader_compositor_blur_common.glsl
@@ -604,6 +644,7 @@ set(SRC_SHADER_CREATE_INFOS
shaders/infos/gpu_shader_3D_smooth_color_info.hh
shaders/infos/gpu_shader_3D_uniform_color_info.hh
shaders/infos/gpu_shader_gpencil_stroke_info.hh
+ shaders/infos/gpu_shader_icon_info.hh
shaders/infos/gpu_shader_instance_varying_color_varying_size_info.hh
shaders/infos/gpu_shader_keyframe_shape_info.hh
shaders/infos/gpu_shader_line_dashed_uniform_color_info.hh
@@ -629,6 +670,7 @@ set(SRC_SHADER_CREATE_INFOS
shaders/compositor/infos/compositor_morphological_distance_info.hh
shaders/compositor/infos/compositor_morphological_distance_threshold_info.hh
shaders/compositor/infos/compositor_morphological_step_info.hh
+ shaders/compositor/infos/compositor_normalize_info.hh
shaders/compositor/infos/compositor_parallel_reduction_info.hh
shaders/compositor/infos/compositor_projector_lens_distortion_info.hh
shaders/compositor/infos/compositor_realize_on_domain_info.hh
@@ -637,6 +679,8 @@ set(SRC_SHADER_CREATE_INFOS
shaders/compositor/infos/compositor_split_viewer_info.hh
shaders/compositor/infos/compositor_symmetric_blur_info.hh
shaders/compositor/infos/compositor_symmetric_separable_blur_info.hh
+ shaders/compositor/infos/compositor_tone_map_photoreceptor_info.hh
+ shaders/compositor/infos/compositor_tone_map_simple_info.hh
)
set(SRC_SHADER_CREATE_INFOS_MTL
diff --git a/source/blender/gpu/GPU_context.h b/source/blender/gpu/GPU_context.h
index b59ea9e55d2..ac82774039a 100644
--- a/source/blender/gpu/GPU_context.h
+++ b/source/blender/gpu/GPU_context.h
@@ -21,6 +21,8 @@ extern "C" {
* automatically initializes the back-end, and #GPU_context_discard frees it when there
* are no more contexts. */
bool GPU_backend_supported(void);
+void GPU_backend_type_selection_set(const eGPUBackendType backend);
+eGPUBackendType GPU_backend_type_selection_get(void);
eGPUBackendType GPU_backend_get_type(void);
/** Opaque type hiding blender::gpu::Context. */
diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h
index bdb384c16f1..917407eece3 100644
--- a/source/blender/gpu/GPU_framebuffer.h
+++ b/source/blender/gpu/GPU_framebuffer.h
@@ -47,6 +47,9 @@ typedef struct GPUOffScreen GPUOffScreen;
GPUFrameBuffer *GPU_framebuffer_create(const char *name);
void GPU_framebuffer_free(GPUFrameBuffer *fb);
void GPU_framebuffer_bind(GPUFrameBuffer *fb);
+
+const char *GPU_framebuffer_get_name(GPUFrameBuffer *fb);
+
/**
* Workaround for binding a SRGB frame-buffer without doing the SRGB transform.
*/
diff --git a/source/blender/gpu/GPU_platform.h b/source/blender/gpu/GPU_platform.h
index b63fe4c0580..657b45df1a5 100644
--- a/source/blender/gpu/GPU_platform.h
+++ b/source/blender/gpu/GPU_platform.h
@@ -16,6 +16,7 @@ typedef enum eGPUBackendType {
GPU_BACKEND_NONE = 0,
GPU_BACKEND_OPENGL = 1 << 0,
GPU_BACKEND_METAL = 1 << 1,
+ GPU_BACKEND_VULKAN = 1 << 3,
GPU_BACKEND_ANY = 0xFFFFFFFFu
} eGPUBackendType;
diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h
index 3f35db42eb9..1148207fc57 100644
--- a/source/blender/gpu/GPU_shader.h
+++ b/source/blender/gpu/GPU_shader.h
@@ -209,6 +209,10 @@ typedef enum eGPUBuiltinShader {
GPU_SHADER_KEYFRAME_SHAPE,
GPU_SHADER_SIMPLE_LIGHTING,
/**
+ * Draw an icon, leaving a semi-transparent rectangle on top of the icon.
+ */
+ GPU_SHADER_ICON,
+ /**
* Take a 2D position and color for each vertex with linear interpolation in window space.
*
* \param color: in vec4
diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc
index 48d7b2019c5..7e94538892a 100644
--- a/source/blender/gpu/intern/gpu_context.cc
+++ b/source/blender/gpu/intern/gpu_context.cc
@@ -33,6 +33,9 @@
# include "gl_backend.hh"
# include "gl_context.hh"
#endif
+#ifdef WITH_VULKAN_BACKEND
+# include "vk_backend.hh"
+#endif
#ifdef WITH_METAL_BACKEND
# include "mtl_backend.hh"
#endif
@@ -223,9 +226,19 @@ void GPU_render_step()
/* NOTE: To enable Metal API, we need to temporarily change this to `GPU_BACKEND_METAL`.
* Until a global switch is added, Metal also needs to be enabled in GHOST_ContextCGL:
* `m_useMetalForRendering = true`. */
-static const eGPUBackendType g_backend_type = GPU_BACKEND_OPENGL;
+static eGPUBackendType g_backend_type = GPU_BACKEND_OPENGL;
static GPUBackend *g_backend = nullptr;
+void GPU_backend_type_selection_set(const eGPUBackendType backend)
+{
+ g_backend_type = backend;
+}
+
+eGPUBackendType GPU_backend_type_selection_get()
+{
+ return g_backend_type;
+}
+
bool GPU_backend_supported(void)
{
switch (g_backend_type) {
@@ -235,6 +248,12 @@ bool GPU_backend_supported(void)
#else
return false;
#endif
+ case GPU_BACKEND_VULKAN:
+#ifdef WITH_VULKAN_BACKEND
+ return true;
+#else
+ return false;
+#endif
case GPU_BACKEND_METAL:
#ifdef WITH_METAL_BACKEND
return MTLBackend::metal_is_supported();
@@ -258,6 +277,11 @@ static void gpu_backend_create()
g_backend = new GLBackend;
break;
#endif
+#ifdef WITH_VULKAN_BACKEND
+ case GPU_BACKEND_VULKAN:
+ g_backend = new VKBackend;
+ break;
+#endif
#ifdef WITH_METAL_BACKEND
case GPU_BACKEND_METAL:
g_backend = new MTLBackend;
diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc
index 6528f39d4ec..5b50fd66196 100644
--- a/source/blender/gpu/intern/gpu_framebuffer.cc
+++ b/source/blender/gpu/intern/gpu_framebuffer.cc
@@ -238,6 +238,11 @@ void GPU_framebuffer_free(GPUFrameBuffer *gpu_fb)
delete unwrap(gpu_fb);
}
+const char *GPU_framebuffer_get_name(GPUFrameBuffer *gpu_fb)
+{
+ return unwrap(gpu_fb)->name_get();
+}
+
/* ---------- Binding ----------- */
void GPU_framebuffer_bind(GPUFrameBuffer *gpu_fb)
diff --git a/source/blender/gpu/intern/gpu_framebuffer_private.hh b/source/blender/gpu/intern/gpu_framebuffer_private.hh
index 76e816e7f65..cb7fd62445c 100644
--- a/source/blender/gpu/intern/gpu_framebuffer_private.hh
+++ b/source/blender/gpu/intern/gpu_framebuffer_private.hh
@@ -95,11 +95,6 @@ class FrameBuffer {
#endif
public:
- /* Reference of a pointer that needs to be cleaned when deallocating the frame-buffer.
- * Points to #BPyGPUFrameBuffer::fb */
- void **ref = nullptr;
-
- public:
FrameBuffer(const char *name);
virtual ~FrameBuffer();
@@ -209,6 +204,11 @@ class FrameBuffer {
{
return attachments_[GPU_FB_COLOR_ATTACHMENT0 + slot].tex;
};
+
+ inline const char *const name_get() const
+ {
+ return name_;
+ };
};
/* Syntactic sugar. */
diff --git a/source/blender/gpu/intern/gpu_immediate.cc b/source/blender/gpu/intern/gpu_immediate.cc
index 3b4accf9cc5..81c0a65bb7c 100644
--- a/source/blender/gpu/intern/gpu_immediate.cc
+++ b/source/blender/gpu/intern/gpu_immediate.cc
@@ -45,7 +45,7 @@ void immBindShader(GPUShader *shader)
BLI_assert(imm->shader == nullptr);
imm->shader = shader;
- imm->builtin_shader_bound = GPU_SHADER_TEXT; /* Default value. */
+ imm->builtin_shader_bound = std::nullopt;
if (!imm->vertex_format.packed) {
VertexFormat_pack(&imm->vertex_format);
@@ -125,9 +125,12 @@ static void wide_line_workaround_start(GPUPrimType prim_type)
/* No need to change the shader. */
return;
}
+ if (!imm->builtin_shader_bound) {
+ return;
+ }
eGPUBuiltinShader polyline_sh;
- switch (imm->builtin_shader_bound) {
+ switch (*imm->builtin_shader_bound) {
case GPU_SHADER_3D_CLIPPED_UNIFORM_COLOR:
polyline_sh = GPU_SHADER_3D_POLYLINE_CLIPPED_UNIFORM_COLOR;
break;
@@ -180,8 +183,8 @@ static void wide_line_workaround_end()
}
immUnbindProgram();
- immBindBuiltinProgram(imm->prev_builtin_shader);
- imm->prev_builtin_shader = GPU_SHADER_TEXT;
+ immBindBuiltinProgram(*imm->prev_builtin_shader);
+ imm->prev_builtin_shader = std::nullopt;
}
}
diff --git a/source/blender/gpu/intern/gpu_immediate_private.hh b/source/blender/gpu/intern/gpu_immediate_private.hh
index 74ebbdc7ae3..c4e11e7082b 100644
--- a/source/blender/gpu/intern/gpu_immediate_private.hh
+++ b/source/blender/gpu/intern/gpu_immediate_private.hh
@@ -9,6 +9,8 @@
#pragma once
+#include <optional>
+
#include "GPU_batch.h"
#include "GPU_primitive.h"
#include "GPU_shader.h"
@@ -42,9 +44,9 @@ class Immediate {
/** Wide Line workaround. */
/** Previously bound shader to restore after drawing. */
- eGPUBuiltinShader prev_builtin_shader = GPU_SHADER_TEXT;
- /** Builtin shader index. Used to test if the workaround can be done. */
- eGPUBuiltinShader builtin_shader_bound = GPU_SHADER_TEXT;
+ std::optional<eGPUBuiltinShader> prev_builtin_shader;
+ /** Builtin shader index. Used to test if the line width workaround can be done. */
+ std::optional<eGPUBuiltinShader> builtin_shader_bound;
/** Uniform color: Kept here to update the wide-line shader just before #immBegin. */
float uniform_color[4];
diff --git a/source/blender/gpu/intern/gpu_shader_builder.cc b/source/blender/gpu/intern/gpu_shader_builder.cc
index 3aa2963ecd0..abb45ca074a 100644
--- a/source/blender/gpu/intern/gpu_shader_builder.cc
+++ b/source/blender/gpu/intern/gpu_shader_builder.cc
@@ -15,6 +15,8 @@
#include "GPU_init_exit.h"
#include "gpu_shader_create_info_private.hh"
+#include "BLI_vector.hh"
+
#include "CLG_log.h"
namespace blender::gpu::shader_builder {
@@ -41,6 +43,22 @@ void ShaderBuilder::init()
CLG_init();
GHOST_GLSettings glSettings = {0};
+ switch (GPU_backend_type_selection_get()) {
+ case GPU_BACKEND_OPENGL:
+ glSettings.context_type = GHOST_kDrawingContextTypeOpenGL;
+ break;
+
+#ifdef WITH_METAL_BACKEND
+ case GPU_BACKEND_METAL:
+ glSettings.context_type = GHOST_kDrawingContextTypeMetal;
+ break;
+#endif
+
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+
ghost_system_ = GHOST_CreateSystem();
ghost_context_ = GHOST_CreateOpenGLContext(ghost_system_, glSettings);
GHOST_ActivateOpenGLContext(ghost_context_);
@@ -73,13 +91,32 @@ int main(int argc, const char *argv[])
int exit_code = 0;
- blender::gpu::shader_builder::ShaderBuilder builder;
- builder.init();
- if (!builder.bake_create_infos()) {
- exit_code = 1;
+ struct NamedBackend {
+ std::string name;
+ eGPUBackendType backend;
+ };
+
+ blender::Vector<NamedBackend> backends_to_validate;
+ backends_to_validate.append({"OpenGL", GPU_BACKEND_OPENGL});
+#ifdef WITH_METAL_BACKEND
+ backends_to_validate.append({"Metal", GPU_BACKEND_METAL});
+#endif
+ for (NamedBackend &backend : backends_to_validate) {
+ GPU_backend_type_selection_set(backend.backend);
+ if (!GPU_backend_supported()) {
+ printf("%s isn't supported on this platform. Shader compilation is skipped\n",
+ backend.name.c_str());
+ continue;
+ }
+ blender::gpu::shader_builder::ShaderBuilder builder;
+ builder.init();
+ if (!builder.bake_create_infos()) {
+ printf("Shader compilation failed for %s backend\n", backend.name.c_str());
+ exit_code = 1;
+ }
+ builder.exit();
}
- builder.exit();
- exit(exit_code);
+ exit(exit_code);
return exit_code;
}
diff --git a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc
index 7a06ede5c6d..65bda7ba858 100644
--- a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc
+++ b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc
@@ -46,6 +46,15 @@ void IMB_freeImBuf(ImBuf * /*ibuf*/)
BLI_assert_unreachable();
}
+struct ImBuf *IMB_allocImBuf(unsigned int /*x*/,
+ unsigned int /*y*/,
+ unsigned char /*planes*/,
+ unsigned int /*flags*/)
+{
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c
index 8a6586e06f6..470643ba863 100644
--- a/source/blender/gpu/intern/gpu_shader_builtin.c
+++ b/source/blender/gpu/intern/gpu_shader_builtin.c
@@ -153,6 +153,11 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = {
.create_info = "gpu_shader_2D_diag_stripes",
},
+ [GPU_SHADER_ICON] =
+ {
+ .name = "GPU_SHADER_ICON",
+ .create_info = "gpu_shader_icon",
+ },
[GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE] =
{
.name = "GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE",
diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh
index b96a9b870e5..124b1751b96 100644
--- a/source/blender/gpu/intern/gpu_texture_private.hh
+++ b/source/blender/gpu/intern/gpu_texture_private.hh
@@ -431,15 +431,16 @@ inline bool validate_data_format(eGPUTextureFormat tex_format, eGPUDataFormat da
case GPU_DEPTH_COMPONENT24:
case GPU_DEPTH_COMPONENT16:
case GPU_DEPTH_COMPONENT32F:
- return data_format == GPU_DATA_FLOAT;
+ return ELEM(data_format, GPU_DATA_FLOAT, GPU_DATA_UINT);
case GPU_DEPTH24_STENCIL8:
case GPU_DEPTH32F_STENCIL8:
- return data_format == GPU_DATA_UINT_24_8;
+ return ELEM(data_format, GPU_DATA_UINT_24_8, GPU_DATA_UINT);
case GPU_R8UI:
case GPU_R16UI:
case GPU_RG16UI:
case GPU_R32UI:
return data_format == GPU_DATA_UINT;
+ case GPU_R32I:
case GPU_RG16I:
case GPU_R16I:
return data_format == GPU_DATA_INT;
@@ -453,6 +454,8 @@ inline bool validate_data_format(eGPUTextureFormat tex_format, eGPUDataFormat da
return ELEM(data_format, GPU_DATA_2_10_10_10_REV, GPU_DATA_FLOAT);
case GPU_R11F_G11F_B10F:
return ELEM(data_format, GPU_DATA_10_11_11_REV, GPU_DATA_FLOAT);
+ case GPU_RGBA16F:
+ return ELEM(data_format, GPU_DATA_HALF_FLOAT, GPU_DATA_FLOAT);
default:
return data_format == GPU_DATA_FLOAT;
}
@@ -585,7 +588,7 @@ inline eGPUFrameBufferBits to_framebuffer_bits(eGPUTextureFormat tex_format)
static inline eGPUTextureFormat to_texture_format(const GPUVertFormat *format)
{
- if (format->attr_len > 1 || format->attr_len == 0) {
+ if (format->attr_len == 0) {
BLI_assert_msg(0, "Incorrect vertex format for buffer texture");
return GPU_DEPTH_COMPONENT24;
}
diff --git a/source/blender/gpu/intern/gpu_vertex_format.cc b/source/blender/gpu/intern/gpu_vertex_format.cc
index b30e3c358c8..76d95ac1b55 100644
--- a/source/blender/gpu/intern/gpu_vertex_format.cc
+++ b/source/blender/gpu/intern/gpu_vertex_format.cc
@@ -361,8 +361,12 @@ void VertexFormat_texture_buffer_pack(GPUVertFormat *format)
* minimum per-vertex stride, which mandates 4-byte alignment in Metal.
* This additional alignment padding caused smaller data types, e.g. U16,
* to mis-align. */
- BLI_assert_msg(format->attr_len == 1,
- "Texture buffer mode should only use a single vertex attribute.");
+ for (int i = 0; i < format->attr_len; i++) {
+ /* The buffer texture setup uses the first attribute for type and size.
+ * Make sure all attributes use the same size. */
+ BLI_assert_msg(format->attrs[i].size == format->attrs[0].size,
+ "Texture buffer mode should only use a attributes with the same size.");
+ }
/* Pack vertex format without minimum stride, as this is not required by texture buffers. */
VertexFormat_pack_impl(format, 1);
diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c
index 71bdf9e336b..e267d5a2f12 100644
--- a/source/blender/gpu/intern/gpu_viewport.c
+++ b/source/blender/gpu/intern/gpu_viewport.c
@@ -147,6 +147,10 @@ static void gpu_viewport_textures_create(GPUViewport *viewport)
if (viewport->depth_tx == NULL) {
viewport->depth_tx = GPU_texture_create_2d(
"dtxl_depth", UNPACK2(size), 1, GPU_DEPTH24_STENCIL8, NULL);
+ if (GPU_clear_viewport_workaround()) {
+ static int depth_clear = 0;
+ GPU_texture_clear(viewport->depth_tx, GPU_DATA_UINT_24_8, &depth_clear);
+ }
}
if (!viewport->depth_tx || !viewport->color_render_tx[0] || !viewport->color_overlay_tx[0]) {
diff --git a/source/blender/gpu/metal/mtl_backend.mm b/source/blender/gpu/metal/mtl_backend.mm
index 2ca1fd3f3d0..240951c1ebd 100644
--- a/source/blender/gpu/metal/mtl_backend.mm
+++ b/source/blender/gpu/metal/mtl_backend.mm
@@ -47,13 +47,11 @@ Context *MTLBackend::context_alloc(void *ghost_window, void *ghost_context)
Batch *MTLBackend::batch_alloc()
{
- /* TODO(Metal): Full MTLBatch implementation. */
return new MTLBatch();
};
DrawList *MTLBackend::drawlist_alloc(int list_length)
{
- /* TODO(Metal): Full MTLDrawList implementation. */
return new MTLDrawList(list_length);
};
@@ -420,6 +418,7 @@ void MTLBackend::capabilities_init(MTLContext *ctx)
GCaps.depth_blitting_workaround = false;
GCaps.use_main_context_workaround = false;
GCaps.broken_amd_driver = false;
+ GCaps.clear_viewport_workaround = true;
/* Metal related workarounds. */
/* Minimum per-vertex stride is 4 bytes in Metal.
diff --git a/source/blender/gpu/metal/mtl_batch.hh b/source/blender/gpu/metal/mtl_batch.hh
index 236367bf5a4..9e179e662b5 100644
--- a/source/blender/gpu/metal/mtl_batch.hh
+++ b/source/blender/gpu/metal/mtl_batch.hh
@@ -10,31 +10,126 @@
#pragma once
#include "MEM_guardedalloc.h"
-
#include "gpu_batch_private.hh"
+#include "mtl_index_buffer.hh"
+#include "mtl_primitive.hh"
+#include "mtl_shader.hh"
+#include "mtl_vertex_buffer.hh"
+
+namespace blender::gpu {
+
+class MTLContext;
+class MTLShaderInterface;
+
+#define GPU_VAO_STATIC_LEN 64
-namespace blender {
-namespace gpu {
+struct VertexBufferID {
+ uint32_t id : 16;
+ uint32_t is_instance : 15;
+ uint32_t used : 1;
+};
-/* Pass-through MTLBatch. TODO(Metal): Implement. */
class MTLBatch : public Batch {
+
+ /* Vertex Bind-state Caching for a given shader interface used with the Batch. */
+ struct VertexDescriptorShaderInterfacePair {
+ MTLVertexDescriptor vertex_descriptor{};
+ const ShaderInterface *interface = nullptr;
+ uint16_t attr_mask{};
+ int num_buffers{};
+ VertexBufferID bufferIds[GPU_BATCH_VBO_MAX_LEN] = {};
+ /* Cache life index compares a cache entry with the active MTLBatch state.
+ * This is initially set to the cache life index of MTLBatch. If the batch has been modified,
+ * this index is incremented to cheaply invalidate existing cache entries. */
+ uint32_t cache_life_index = 0;
+ };
+
+ class MTLVertexDescriptorCache {
+
+ private:
+ MTLBatch *batch_;
+
+ VertexDescriptorShaderInterfacePair cache_[GPU_VAO_STATIC_LEN] = {};
+ MTLContext *cache_context_ = nullptr;
+ uint32_t cache_life_index_ = 0;
+
+ public:
+ MTLVertexDescriptorCache(MTLBatch *batch) : batch_(batch){};
+ VertexDescriptorShaderInterfacePair *find(const ShaderInterface *interface);
+ bool insert(VertexDescriptorShaderInterfacePair &data);
+
+ private:
+ void vertex_descriptor_cache_init(MTLContext *ctx);
+ void vertex_descriptor_cache_clear();
+ void vertex_descriptor_cache_ensure();
+ };
+
+ private:
+ MTLShader *active_shader_ = nullptr;
+ bool shader_in_use_ = false;
+ MTLVertexDescriptorCache vao_cache = {this};
+
+ /* Topology emulation. */
+ gpu::MTLBuffer *emulated_topology_buffer_ = nullptr;
+ GPUPrimType emulated_topology_type_;
+ uint32_t topology_buffer_input_v_count_ = 0;
+ uint32_t topology_buffer_output_v_count_ = 0;
+
public:
- void draw(int v_first, int v_count, int i_first, int i_count) override
- {
- }
+ MTLBatch(){};
+ ~MTLBatch(){};
+ void draw(int v_first, int v_count, int i_first, int i_count) override;
void draw_indirect(GPUStorageBuf *indirect_buf, intptr_t offset) override
{
+ /* TODO(Metal): Support indirect draw commands. */
}
-
void multi_draw_indirect(GPUStorageBuf *indirect_buf,
int count,
intptr_t offset,
intptr_t stride) override
{
+ /* TODO(Metal): Support indirect draw commands. */
+ }
+
+ /* Returns an initialized RenderComandEncoder for drawing if all is good.
+ * Otherwise, nil. */
+ id<MTLRenderCommandEncoder> bind(uint v_first, uint v_count, uint i_first, uint i_count);
+ void unbind();
+
+ /* Convenience getters. */
+ MTLIndexBuf *elem_() const
+ {
+ return static_cast<MTLIndexBuf *>(unwrap(elem));
+ }
+ MTLVertBuf *verts_(const int index) const
+ {
+ return static_cast<MTLVertBuf *>(unwrap(verts[index]));
}
+ MTLVertBuf *inst_(const int index) const
+ {
+ return static_cast<MTLVertBuf *>(unwrap(inst[index]));
+ }
+ MTLShader *active_shader_get() const
+ {
+ return active_shader_;
+ }
+
+ private:
+ void shader_bind();
+ void draw_advanced(int v_first, int v_count, int i_first, int i_count);
+ int prepare_vertex_binding(MTLVertBuf *verts,
+ MTLRenderPipelineStateDescriptor &desc,
+ const MTLShaderInterface *interface,
+ uint16_t &attr_mask,
+ bool instanced);
+
+ id<MTLBuffer> get_emulated_toplogy_buffer(GPUPrimType &in_out_prim_type, uint32_t &v_count);
+
+ void prepare_vertex_descriptor_and_bindings(
+ MTLVertBuf **buffers, int &num_buffers, int v_first, int v_count, int i_first, int i_count);
+
MEM_CXX_CLASS_ALLOC_FUNCS("MTLBatch");
};
-} // namespace gpu
-} // namespace blender
+} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_batch.mm b/source/blender/gpu/metal/mtl_batch.mm
new file mode 100644
index 00000000000..988fb9b793b
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_batch.mm
@@ -0,0 +1,998 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ *
+ * Metal implementation of GPUBatch.
+ */
+
+#include "BLI_assert.h"
+#include "BLI_span.hh"
+
+#include "BKE_global.h"
+
+#include "GPU_common.h"
+#include "gpu_batch_private.hh"
+#include "gpu_shader_private.hh"
+
+#include "mtl_batch.hh"
+#include "mtl_context.hh"
+#include "mtl_debug.hh"
+#include "mtl_index_buffer.hh"
+#include "mtl_shader.hh"
+#include "mtl_vertex_buffer.hh"
+
+#include <string>
+
+namespace blender::gpu {
+
+/* -------------------------------------------------------------------- */
+/** \name Creation & Deletion
+ * \{ */
+void MTLBatch::draw(int v_first, int v_count, int i_first, int i_count)
+{
+ if (this->flag & GPU_BATCH_INVALID) {
+ this->shader_in_use_ = false;
+ }
+ this->draw_advanced(v_first, v_count, i_first, i_count);
+}
+
+void MTLBatch::shader_bind()
+{
+ if (active_shader_ && active_shader_->is_valid()) {
+ active_shader_->bind();
+ shader_in_use_ = true;
+ }
+}
+
+void MTLBatch::MTLVertexDescriptorCache::vertex_descriptor_cache_init(MTLContext *ctx)
+{
+ BLI_assert(ctx != nullptr);
+ this->vertex_descriptor_cache_clear();
+ cache_context_ = ctx;
+}
+
+void MTLBatch::MTLVertexDescriptorCache::vertex_descriptor_cache_clear()
+{
+ cache_life_index_++;
+ cache_context_ = nullptr;
+}
+
+void MTLBatch::MTLVertexDescriptorCache::vertex_descriptor_cache_ensure()
+{
+ if (this->cache_context_ != nullptr) {
+
+ /* Invalidate vertex descriptor bindings cache if batch has changed. */
+ if (batch_->flag & GPU_BATCH_DIRTY) {
+ batch_->flag &= ~GPU_BATCH_DIRTY;
+ this->vertex_descriptor_cache_clear();
+ }
+ }
+
+ /* Initialize cache if not ready. */
+ if (cache_context_ == nullptr) {
+ this->vertex_descriptor_cache_init(MTLContext::get());
+ }
+}
+
+MTLBatch::VertexDescriptorShaderInterfacePair *MTLBatch::MTLVertexDescriptorCache::find(
+ const ShaderInterface *interface)
+{
+ this->vertex_descriptor_cache_ensure();
+ for (int i = 0; i < GPU_VAO_STATIC_LEN; ++i) {
+ if (cache_[i].interface == interface && cache_[i].cache_life_index == cache_life_index_) {
+ return &cache_[i];
+ }
+ }
+ return nullptr;
+}
+
+bool MTLBatch::MTLVertexDescriptorCache::insert(
+ MTLBatch::VertexDescriptorShaderInterfacePair &data)
+{
+ vertex_descriptor_cache_ensure();
+ for (int i = 0; i < GPU_VAO_STATIC_LEN; ++i) {
+ if (cache_[i].interface == nullptr || cache_[i].cache_life_index != cache_life_index_) {
+ cache_[i] = data;
+ cache_[i].cache_life_index = cache_life_index_;
+ return true;
+ }
+ }
+ return false;
+}
+
+int MTLBatch::prepare_vertex_binding(MTLVertBuf *verts,
+ MTLRenderPipelineStateDescriptor &desc,
+ const MTLShaderInterface *interface,
+ uint16_t &attr_mask,
+ bool instanced)
+{
+
+ const GPUVertFormat *format = &verts->format;
+ /* Whether the current vertex buffer has been added to the buffer layout descriptor. */
+ bool buffer_added = false;
+ /* Per-vertex stride of current vertex buffer. */
+ int buffer_stride = format->stride;
+ /* Buffer binding index of the vertex buffer once added to the buffer layout descriptor. */
+ int buffer_index = -1;
+ int attribute_offset = 0;
+
+ if (!active_shader_->get_uses_ssbo_vertex_fetch()) {
+ BLI_assert(
+ buffer_stride >= 4 &&
+ "In Metal, Vertex buffer stride should be 4. SSBO Vertex fetch is not affected by this");
+ }
+
+ /* Iterate over GPUVertBuf vertex format and find attributes matching those in the active
+ * shader's interface. */
+ for (uint32_t a_idx = 0; a_idx < format->attr_len; a_idx++) {
+ const GPUVertAttr *a = &format->attrs[a_idx];
+
+ if (format->deinterleaved) {
+ attribute_offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].size) * verts->vertex_len;
+ buffer_stride = a->size;
+ }
+ else {
+ attribute_offset = a->offset;
+ }
+
+ /* Find attribute with the matching name. Attributes may have multiple compatible
+ * name aliases. */
+ for (uint32_t n_idx = 0; n_idx < a->name_len; n_idx++) {
+ const char *name = GPU_vertformat_attr_name_get(format, a, n_idx);
+ const ShaderInput *input = interface->attr_get(name);
+
+ if (input == nullptr || input->location == -1) {
+ /* Vertex/instance buffers provided have attribute data for attributes which are not needed
+ * by this particular shader. This shader only needs binding information for the attributes
+ * has in the shader interface. */
+ MTL_LOG_WARNING(
+ "MTLBatch: Could not find attribute with name '%s' (defined in active vertex format) "
+ "in the shader interface for shader '%s'\n",
+ name,
+ interface->get_name());
+ continue;
+ }
+
+ /* Fetch metal attribute information. */
+ const MTLShaderInputAttribute &mtl_attr = interface->get_attribute(input->location);
+ BLI_assert(mtl_attr.location >= 0);
+ /* Verify that the attribute location from the shader interface
+ * matches the attribute location returned. */
+ BLI_assert(mtl_attr.location == input->location);
+
+ /* Check if attribute is already present in the given slot. */
+ if ((~attr_mask) & (1 << mtl_attr.location)) {
+ MTL_LOG_INFO(
+ " -- [Batch] Skipping attribute with input location %d (As one is already bound)\n",
+ mtl_attr.location);
+ }
+ else {
+
+ /* Update attribute used-slot mask. */
+ attr_mask &= ~(1 << mtl_attr.location);
+
+ /* Add buffer layout entry in descriptor if it has not yet been added
+ * for current vertex buffer. */
+ if (!buffer_added) {
+ buffer_index = desc.vertex_descriptor.num_vert_buffers;
+ desc.vertex_descriptor.buffer_layouts[buffer_index].step_function =
+ (instanced) ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
+ desc.vertex_descriptor.buffer_layouts[buffer_index].step_rate = 1;
+ desc.vertex_descriptor.buffer_layouts[buffer_index].stride = buffer_stride;
+ desc.vertex_descriptor.num_vert_buffers++;
+ buffer_added = true;
+
+ MTL_LOG_INFO(" -- [Batch] Adding source %s buffer (Index: %d, Stride: %d)\n",
+ (instanced) ? "instance" : "vertex",
+ buffer_index,
+ buffer_stride);
+ }
+ else {
+ /* Ensure stride is correct for de-interleaved attributes. */
+ desc.vertex_descriptor.buffer_layouts[buffer_index].stride = buffer_stride;
+ }
+
+ /* Handle Matrix/Array vertex attribute types.
+ * Metal does not natively support these as attribute types, so we handle these cases
+ * by stacking together compatible types (e.g. 4xVec4 for Mat4) and combining
+ * the data in the shader.
+ * The generated Metal shader will contain a generated input binding, which reads
+ * in individual attributes and merges them into the desired type after vertex
+ * assembly. e.g. a Mat4 (Float4x4) will generate 4 Float4 attributes. */
+ if (a->comp_len == 16 || a->comp_len == 12 || a->comp_len == 8) {
+ BLI_assert_msg(
+ a->comp_len == 16,
+ "only mat4 attributes currently supported -- Not ready to handle other long "
+ "component length attributes yet");
+
+ /* SSBO Vertex Fetch Attribute safety checks. */
+ if (active_shader_->get_uses_ssbo_vertex_fetch()) {
+ /* When using SSBO vertex fetch, we do not need to expose split attributes,
+ * A matrix can be read directly as a whole block of contiguous data. */
+ MTLSSBOAttribute ssbo_attr(mtl_attr.index,
+ buffer_index,
+ attribute_offset,
+ buffer_stride,
+ GPU_SHADER_ATTR_TYPE_MAT4,
+ instanced);
+ active_shader_->ssbo_vertex_fetch_bind_attribute(ssbo_attr);
+ desc.vertex_descriptor.ssbo_attributes[desc.vertex_descriptor.num_ssbo_attributes] =
+ ssbo_attr;
+ desc.vertex_descriptor.num_ssbo_attributes++;
+ }
+ else {
+
+ /* Handle Mat4 attributes. */
+ if (a->comp_len == 16) {
+ /* Debug safety checks. */
+ BLI_assert_msg(mtl_attr.matrix_element_count == 4,
+ "mat4 type expected but there are fewer components");
+ BLI_assert_msg(mtl_attr.size == 16, "Expecting subtype 'vec4' with 16 bytes");
+ BLI_assert_msg(
+ mtl_attr.format == MTLVertexFormatFloat4,
+ "Per-attribute vertex format MUST be float4 for an input type of 'mat4'");
+
+ /* We have found the 'ROOT' attribute. A mat4 contains 4 consecutive float4 attribute
+ * locations we must map to. */
+ for (int i = 0; i < a->comp_len / 4; i++) {
+ desc.vertex_descriptor.attributes[mtl_attr.location + i].format =
+ MTLVertexFormatFloat4;
+ /* Data is consecutive in the buffer for the whole matrix, each float4 will shift
+ * the offset by 16 bytes. */
+ desc.vertex_descriptor.attributes[mtl_attr.location + i].offset =
+ attribute_offset + i * 16;
+ /* All source data for a matrix is in the same singular buffer. */
+ desc.vertex_descriptor.attributes[mtl_attr.location + i].buffer_index =
+ buffer_index;
+
+ /* Update total attribute account. */
+ desc.vertex_descriptor.num_attributes = max_ii(
+ mtl_attr.location + i + 1, desc.vertex_descriptor.num_attributes);
+ MTL_LOG_INFO("-- Sub-Attrib Location: %d, offset: %d, buffer index: %d\n",
+ mtl_attr.location + i,
+ attribute_offset + i * 16,
+ buffer_index);
+ }
+ MTL_LOG_INFO(
+ "Float4x4 attribute type added for '%s' at attribute locations: %d to %d\n",
+ name,
+ mtl_attr.location,
+ mtl_attr.location + 3);
+ }
+
+ /* Ensure we are not exceeding the attribute limit. */
+ BLI_assert(desc.vertex_descriptor.num_attributes <= MTL_MAX_VERTEX_INPUT_ATTRIBUTES);
+ }
+ }
+ else {
+
+ /* Handle Any required format conversions.
+ * NOTE(Metal): If there is a mis-match between the format of an attribute
+ * in the shader interface, and the specified format in the VertexBuffer VertexFormat,
+ * we need to perform a format conversion.
+ *
+ * The Metal API can perform certain conversions internally during vertex assembly:
+ * - Type Normalization e.g short2 to float2 between 0.0 to 1.0.
+ * - Type Truncation e.g. Float4 to Float2.
+ * - Type expansion e,g, Float3 to Float4 (Following 0,0,0,1 for assignment to empty
+ * elements).
+ *
+ * Certain conversion cannot be performed however, and in these cases, we need to
+ * instruct the shader to generate a specialized version with a conversion routine upon
+ * attribute read.
+ * - This handles cases such as conversion between types e.g. Integer to float without
+ * normalization.
+ *
+ * For more information on the supported and unsupported conversions, see:
+ * https://developer.apple.com/documentation/metal/mtlvertexattributedescriptor/1516081-format?language=objc
+ */
+ MTLVertexFormat converted_format;
+ bool can_use_internal_conversion = mtl_convert_vertex_format(
+ mtl_attr.format,
+ (GPUVertCompType)a->comp_type,
+ a->comp_len,
+ (GPUVertFetchMode)a->fetch_mode,
+ &converted_format);
+ bool is_floating_point_format = (a->comp_type == GPU_COMP_F32);
+
+ if (can_use_internal_conversion) {
+ desc.vertex_descriptor.attributes[mtl_attr.location].format = converted_format;
+ desc.vertex_descriptor.attributes[mtl_attr.location].format_conversion_mode =
+ is_floating_point_format ? (GPUVertFetchMode)GPU_FETCH_FLOAT :
+ (GPUVertFetchMode)GPU_FETCH_INT;
+ BLI_assert(converted_format != MTLVertexFormatInvalid);
+ }
+ else {
+ /* The internal implicit conversion is not supported.
+ * In this case, we need to handle conversion inside the shader.
+ * This is handled using `format_conversion_mode`.
+ * `format_conversion_mode` is assigned the blender-specified fetch mode (GPU_FETCH_*).
+ * This then controls how a given attribute is interpreted. The data will be read
+ * as specified and then converted appropriately to the correct form.
+ *
+ * e.g. if `GPU_FETCH_INT_TO_FLOAT` is specified, the specialized read-routine
+ * in the shader will read the data as an int, and cast this to floating point
+ * representation. (Rather than reading the source data as float).
+ *
+ * NOTE: Even if full conversion is not supported, we may still partially perform an
+ * implicit conversion where possible, such as vector truncation or expansion. */
+ MTLVertexFormat converted_format;
+ bool can_convert = mtl_vertex_format_resize(
+ mtl_attr.format, a->comp_len, &converted_format);
+ desc.vertex_descriptor.attributes[mtl_attr.location].format = can_convert ?
+ converted_format :
+ mtl_attr.format;
+ desc.vertex_descriptor.attributes[mtl_attr.location].format_conversion_mode =
+ (GPUVertFetchMode)a->fetch_mode;
+ BLI_assert(desc.vertex_descriptor.attributes[mtl_attr.location].format !=
+ MTLVertexFormatInvalid);
+ }
+ desc.vertex_descriptor.attributes[mtl_attr.location].offset = attribute_offset;
+ desc.vertex_descriptor.attributes[mtl_attr.location].buffer_index = buffer_index;
+ desc.vertex_descriptor.num_attributes = ((mtl_attr.location + 1) >
+ desc.vertex_descriptor.num_attributes) ?
+ (mtl_attr.location + 1) :
+ desc.vertex_descriptor.num_attributes;
+
+ /* SSBO Vertex Fetch attribute bind. */
+ if (active_shader_->get_uses_ssbo_vertex_fetch()) {
+ BLI_assert_msg(desc.vertex_descriptor.attributes[mtl_attr.location].format ==
+ mtl_attr.format,
+ "SSBO Vertex Fetch does not support attribute conversion.");
+
+ MTLSSBOAttribute ssbo_attr(
+ mtl_attr.index,
+ buffer_index,
+ attribute_offset,
+ buffer_stride,
+ MTLShader::ssbo_vertex_type_to_attr_type(
+ desc.vertex_descriptor.attributes[mtl_attr.location].format),
+ instanced);
+
+ active_shader_->ssbo_vertex_fetch_bind_attribute(ssbo_attr);
+ desc.vertex_descriptor.ssbo_attributes[desc.vertex_descriptor.num_ssbo_attributes] =
+ ssbo_attr;
+ desc.vertex_descriptor.num_ssbo_attributes++;
+ }
+
+ /* NOTE: We are setting num_attributes to be up to the maximum found index, because of
+ * this, it is possible that we may skip over certain attributes if they were not in the
+ * source GPUVertFormat. */
+ MTL_LOG_INFO(
+ " -- Batch Attribute(%d): ORIG Shader Format: %d, ORIG Vert format: %d, Vert "
+ "components: %d, Fetch Mode %d --> FINAL FORMAT: %d\n",
+ mtl_attr.location,
+ (int)mtl_attr.format,
+ (int)a->comp_type,
+ (int)a->comp_len,
+ (int)a->fetch_mode,
+ (int)desc.vertex_descriptor.attributes[mtl_attr.location].format);
+
+ MTL_LOG_INFO(
+ " -- [Batch] matching %s attribute '%s' (Attribute Index: %d, Buffer index: %d, "
+ "offset: %d)\n",
+ (instanced) ? "instance" : "vertex",
+ name,
+ mtl_attr.location,
+ buffer_index,
+ attribute_offset);
+ }
+ }
+ }
+ }
+ if (buffer_added) {
+ return buffer_index;
+ }
+ return -1;
+}
+
+id<MTLRenderCommandEncoder> MTLBatch::bind(uint v_first, uint v_count, uint i_first, uint i_count)
+{
+ /* Setup draw call and render pipeline state here. Called by every draw, but setup here so that
+ * MTLDrawList only needs to perform setup a single time. */
+ BLI_assert(this);
+
+ /* Fetch Metal device. */
+ MTLContext *ctx = MTLContext::get();
+ if (!ctx) {
+ BLI_assert_msg(false, "No context available for rendering.");
+ return nil;
+ }
+
+ /* Verify Shader. */
+ active_shader_ = (shader) ? static_cast<MTLShader *>(unwrap(shader)) : nullptr;
+
+ if (active_shader_ == nullptr || !active_shader_->is_valid()) {
+ /* Skip drawing if there is no valid Metal shader.
+ * This will occur if the path through which the shader is prepared
+ * is invalid (e.g. Python without create-info), or, the source shader uses a geometry pass. */
+ BLI_assert_msg(false, "No valid Metal shader!");
+ return nil;
+ }
+
+ /* Check if using SSBO Fetch Mode.
+ * This is an alternative drawing mode to geometry shaders, wherein vertex buffers
+ * are bound as readable (random-access) GPU buffers and certain descriptor properties
+ * are passed using Shader uniforms. */
+ bool uses_ssbo_fetch = active_shader_->get_uses_ssbo_vertex_fetch();
+
+ /* Prepare Vertex Descriptor and extract VertexBuffers to bind. */
+ MTLVertBuf *buffers[GPU_BATCH_VBO_MAX_LEN] = {nullptr};
+ int num_buffers = 0;
+
+ /* Ensure Index Buffer is ready. */
+ MTLIndexBuf *mtl_elem = static_cast<MTLIndexBuf *>(reinterpret_cast<IndexBuf *>(this->elem));
+ if (mtl_elem != NULL) {
+ mtl_elem->upload_data();
+ }
+
+ /* Populate vertex descriptor with attribute binding information.
+ * The vertex descriptor and buffer layout descriptors describe
+ * how vertex data from bound vertex buffers maps to the
+ * shader's input.
+ * A unique vertex descriptor will result in a new PipelineStateObject
+ * being generated for the currently bound shader. */
+ prepare_vertex_descriptor_and_bindings(buffers, num_buffers, v_first, v_count, i_first, i_count);
+
+ /* Prepare Vertex Buffers - Run before RenderCommandEncoder in case BlitCommandEncoder buffer
+ * data operations are required. */
+ for (int i = 0; i < num_buffers; i++) {
+ MTLVertBuf *buf_at_index = buffers[i];
+ if (buf_at_index == NULL) {
+ BLI_assert_msg(
+ false,
+ "Total buffer count does not match highest buffer index, could be gaps in bindings");
+ continue;
+ }
+
+ MTLVertBuf *mtlvbo = static_cast<MTLVertBuf *>(reinterpret_cast<VertBuf *>(buf_at_index));
+ mtlvbo->bind();
+ }
+
+ /* Ensure render pass is active and fetch active RenderCommandEncoder. */
+ id<MTLRenderCommandEncoder> rec = ctx->ensure_begin_render_pass();
+
+ /* Fetch RenderPassState to enable resource binding for active pass. */
+ MTLRenderPassState &rps = ctx->main_command_buffer.get_render_pass_state();
+
+ /* Debug Check: Ensure Frame-buffer instance is not dirty. */
+ BLI_assert(!ctx->main_command_buffer.get_active_framebuffer()->get_dirty());
+
+ /* Bind Shader. */
+ this->shader_bind();
+
+ /* GPU debug markers. */
+ if (G.debug & G_DEBUG_GPU) {
+ [rec pushDebugGroup:[NSString stringWithFormat:@"batch_bind%@(shader: %s)",
+ this->elem ? @"(indexed)" : @"",
+ active_shader_->get_interface()->get_name()]];
+ [rec insertDebugSignpost:[NSString
+ stringWithFormat:@"batch_bind%@(shader: %s)",
+ this->elem ? @"(indexed)" : @"",
+ active_shader_->get_interface()->get_name()]];
+ }
+
+ /* Ensure Context Render Pipeline State is fully setup and ready to execute the draw. */
+ MTLPrimitiveType mtl_prim_type = gpu_prim_type_to_metal(this->prim_type);
+ if (!ctx->ensure_render_pipeline_state(mtl_prim_type)) {
+ printf("FAILED TO ENSURE RENDER PIPELINE STATE");
+ BLI_assert(false);
+
+ if (G.debug & G_DEBUG_GPU) {
+ [rec popDebugGroup];
+ }
+ return nil;
+ }
+
+ /*** Bind Vertex Buffers and Index Buffers **/
+
+ /* SSBO Vertex Fetch Buffer bindings. */
+ if (uses_ssbo_fetch) {
+
+ /* SSBO Vertex Fetch - Bind Index Buffer to appropriate slot -- if used. */
+ id<MTLBuffer> idx_buffer = nil;
+ GPUPrimType final_prim_type = this->prim_type;
+
+ if (mtl_elem != nullptr) {
+
+ /* Fetch index buffer. This function can situationally return an optimized
+ * index buffer of a different primitive type. If this is the case, `final_prim_type`
+ * and `v_count` will be updated with the new format.
+ * NOTE: For indexed rendering, v_count represents the number of indices. */
+ idx_buffer = mtl_elem->get_index_buffer(final_prim_type, v_count);
+ BLI_assert(idx_buffer != nil);
+
+ /* Update uniforms for SSBO-vertex-fetch-mode indexed rendering to flag usage. */
+ int &uniform_ssbo_index_mode_u16 = active_shader_->uni_ssbo_uses_index_mode_u16;
+ BLI_assert(uniform_ssbo_index_mode_u16 != -1);
+ int uses_index_mode_u16 = (mtl_elem->index_type_ == GPU_INDEX_U16) ? 1 : 0;
+ active_shader_->uniform_int(uniform_ssbo_index_mode_u16, 1, 1, &uses_index_mode_u16);
+ }
+ else {
+ idx_buffer = ctx->get_null_buffer();
+ }
+ rps.bind_vertex_buffer(idx_buffer, 0, MTL_SSBO_VERTEX_FETCH_IBO_INDEX);
+
+ /* Ensure all attributes are set */
+ active_shader_->ssbo_vertex_fetch_bind_attributes_end(rec);
+
+ /* Bind NULL Buffers for unused vertex data slots. */
+ id<MTLBuffer> null_buffer = ctx->get_null_buffer();
+ BLI_assert(null_buffer != nil);
+ for (int i = num_buffers; i < MTL_SSBO_VERTEX_FETCH_MAX_VBOS; i++) {
+ if (rps.cached_vertex_buffer_bindings[i].metal_buffer == nil) {
+ rps.bind_vertex_buffer(null_buffer, 0, i);
+ }
+ }
+
+ /* Flag whether Indexed rendering is used or not. */
+ int &uniform_ssbo_use_indexed = active_shader_->uni_ssbo_uses_indexed_rendering;
+ BLI_assert(uniform_ssbo_use_indexed != -1);
+ int uses_indexed_rendering = (mtl_elem != NULL) ? 1 : 0;
+ active_shader_->uniform_int(uniform_ssbo_use_indexed, 1, 1, &uses_indexed_rendering);
+
+ /* Set SSBO-fetch-mode status uniforms. */
+ BLI_assert(active_shader_->uni_ssbo_input_prim_type_loc != -1);
+ BLI_assert(active_shader_->uni_ssbo_input_vert_count_loc != -1);
+ GPU_shader_uniform_vector_int(reinterpret_cast<GPUShader *>(wrap(active_shader_)),
+ active_shader_->uni_ssbo_input_prim_type_loc,
+ 1,
+ 1,
+ (const int *)(&final_prim_type));
+ GPU_shader_uniform_vector_int(reinterpret_cast<GPUShader *>(wrap(active_shader_)),
+ active_shader_->uni_ssbo_input_vert_count_loc,
+ 1,
+ 1,
+ (const int *)(&v_count));
+ }
+
+ /* Bind Vertex Buffers. */
+ for (int i = 0; i < num_buffers; i++) {
+ MTLVertBuf *buf_at_index = buffers[i];
+ if (buf_at_index == NULL) {
+ BLI_assert_msg(
+ false,
+ "Total buffer count does not match highest buffer index, could be gaps in bindings");
+ continue;
+ }
+ /* Buffer handle. */
+ MTLVertBuf *mtlvbo = static_cast<MTLVertBuf *>(reinterpret_cast<VertBuf *>(buf_at_index));
+ mtlvbo->flag_used();
+
+ /* Fetch buffer from MTLVertexBuffer and bind. */
+ id<MTLBuffer> mtl_buffer = mtlvbo->get_metal_buffer();
+
+ BLI_assert(mtl_buffer != nil);
+ rps.bind_vertex_buffer(mtl_buffer, 0, i);
+ }
+
+ if (G.debug & G_DEBUG_GPU) {
+ [rec popDebugGroup];
+ }
+
+ /* Return Render Command Encoder used with setup. */
+ return rec;
+}
+
+void MTLBatch::unbind()
+{
+}
+
+void MTLBatch::prepare_vertex_descriptor_and_bindings(
+ MTLVertBuf **buffers, int &num_buffers, int v_first, int v_count, int i_first, int i_count)
+{
+
+ /* Here we populate the MTLContext vertex descriptor and resolve which buffers need to be bound.
+ */
+ MTLStateManager *state_manager = static_cast<MTLStateManager *>(
+ MTLContext::get()->state_manager);
+ MTLRenderPipelineStateDescriptor &desc = state_manager->get_pipeline_descriptor();
+ const MTLShaderInterface *interface = active_shader_->get_interface();
+ uint16_t attr_mask = interface->get_enabled_attribute_mask();
+
+ /* Reset vertex descriptor to default state. */
+ desc.reset_vertex_descriptor();
+
+ /* Fetch Vertex and Instance Buffers. */
+ Span<MTLVertBuf *> mtl_verts(reinterpret_cast<MTLVertBuf **>(this->verts),
+ GPU_BATCH_VBO_MAX_LEN);
+ Span<MTLVertBuf *> mtl_inst(reinterpret_cast<MTLVertBuf **>(this->inst),
+ GPU_BATCH_INST_VBO_MAX_LEN);
+
+ /* SSBO Vertex fetch also passes vertex descriptor information into the shader. */
+ if (active_shader_->get_uses_ssbo_vertex_fetch()) {
+ active_shader_->ssbo_vertex_fetch_bind_attributes_begin();
+ }
+
+ /* Resolve Metal vertex buffer bindings. */
+ /* Vertex Descriptors
+ * ------------------
+ * Vertex Descriptors are required to generate a pipeline state, based on the current Batch's
+ * buffer bindings. These bindings are a unique matching, depending on what input attributes a
+ * batch has in its buffers, and those which are supported by the shader interface.
+
+ * We iterate through the buffers and resolve which attributes satisfy the requirements of the
+ * currently bound shader. We cache this data, for a given Batch<->ShderInterface pairing in a
+ * VAO cache to avoid the need to recalculate this data. */
+ bool buffer_is_instanced[GPU_BATCH_VBO_MAX_LEN] = {false};
+
+ VertexDescriptorShaderInterfacePair *descriptor = this->vao_cache.find(interface);
+ if (descriptor) {
+ desc.vertex_descriptor = descriptor->vertex_descriptor;
+ attr_mask = descriptor->attr_mask;
+ num_buffers = descriptor->num_buffers;
+
+ for (int bid = 0; bid < GPU_BATCH_VBO_MAX_LEN; ++bid) {
+ if (descriptor->bufferIds[bid].used) {
+ if (descriptor->bufferIds[bid].is_instance) {
+ buffers[bid] = mtl_inst[descriptor->bufferIds[bid].id];
+ buffer_is_instanced[bid] = true;
+ }
+ else {
+ buffers[bid] = mtl_verts[descriptor->bufferIds[bid].id];
+ buffer_is_instanced[bid] = false;
+ }
+ }
+ }
+
+ /* Use cached ssbo attribute binding data. */
+ if (active_shader_->get_uses_ssbo_vertex_fetch()) {
+ BLI_assert(desc.vertex_descriptor.uses_ssbo_vertex_fetch);
+ for (int attr_id = 0; attr_id < desc.vertex_descriptor.num_ssbo_attributes; attr_id++) {
+ active_shader_->ssbo_vertex_fetch_bind_attribute(
+ desc.vertex_descriptor.ssbo_attributes[attr_id]);
+ }
+ }
+ }
+ else {
+ VertexDescriptorShaderInterfacePair pair{};
+ pair.interface = interface;
+
+ for (int i = 0; i < GPU_BATCH_VBO_MAX_LEN; ++i) {
+ pair.bufferIds[i].id = -1;
+ pair.bufferIds[i].is_instance = 0;
+ pair.bufferIds[i].used = 0;
+ }
+ /* NOTE: Attribute extraction order from buffer is the reverse of the OpenGL as we flag once an
+ * attribute is found, rather than pre-setting the mask. */
+ /* Extract Instance attributes (These take highest priority). */
+ for (int v = 0; v < GPU_BATCH_INST_VBO_MAX_LEN; v++) {
+ if (mtl_inst[v]) {
+ MTL_LOG_INFO(" -- [Batch] Checking bindings for bound instance buffer %p\n", mtl_inst[v]);
+ int buffer_ind = this->prepare_vertex_binding(
+ mtl_inst[v], desc, interface, attr_mask, true);
+ if (buffer_ind >= 0) {
+ buffers[buffer_ind] = mtl_inst[v];
+ buffer_is_instanced[buffer_ind] = true;
+
+ pair.bufferIds[buffer_ind].id = v;
+ pair.bufferIds[buffer_ind].used = 1;
+ pair.bufferIds[buffer_ind].is_instance = 1;
+ num_buffers = ((buffer_ind + 1) > num_buffers) ? (buffer_ind + 1) : num_buffers;
+ }
+ }
+ }
+
+ /* Extract Vertex attributes (First-bound vertex buffer takes priority). */
+ for (int v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) {
+ if (mtl_verts[v] != NULL) {
+ MTL_LOG_INFO(" -- [Batch] Checking bindings for bound vertex buffer %p\n", mtl_verts[v]);
+ int buffer_ind = this->prepare_vertex_binding(
+ mtl_verts[v], desc, interface, attr_mask, false);
+ if (buffer_ind >= 0) {
+ buffers[buffer_ind] = mtl_verts[v];
+ buffer_is_instanced[buffer_ind] = false;
+
+ pair.bufferIds[buffer_ind].id = v;
+ pair.bufferIds[buffer_ind].used = 1;
+ pair.bufferIds[buffer_ind].is_instance = 0;
+ num_buffers = ((buffer_ind + 1) > num_buffers) ? (buffer_ind + 1) : num_buffers;
+ }
+ }
+ }
+
+ /* Add to VertexDescriptor cache */
+ desc.vertex_descriptor.uses_ssbo_vertex_fetch = active_shader_->get_uses_ssbo_vertex_fetch();
+ pair.attr_mask = attr_mask;
+ pair.vertex_descriptor = desc.vertex_descriptor;
+ pair.num_buffers = num_buffers;
+ if (!this->vao_cache.insert(pair)) {
+ printf(
+ "[Performance Warning] cache is full (Size: %d), vertex descriptor will not be cached\n",
+ GPU_VAO_STATIC_LEN);
+ }
+ }
+
+/* DEBUG: verify if our attribute bindings have been fully provided as expected. */
+#if MTL_DEBUG_SHADER_ATTRIBUTES == 1
+ if (attr_mask != 0) {
+ for (uint16_t mask = 1, a = 0; a < 16; a++, mask <<= 1) {
+ if (attr_mask & mask) {
+ /* Fallback for setting default attributes, for missed slots. Attributes flagged with
+ * 'MTLVertexFormatInvalid' in the vertex descriptor are bound to a NULL buffer during PSO
+ * creation. */
+ MTL_LOG_WARNING("MTLBatch: Missing expected attribute '%s' at index '%d' for shader: %s\n",
+ this->active_shader->interface->attributes[a].name,
+ a,
+ interface->name);
+ /* Ensure any assigned attribute has not been given an invalid format. This should not
+ * occur and may be the result of an unsupported attribute type conversion. */
+ BLI_assert(desc.attributes[a].format == MTLVertexFormatInvalid);
+ }
+ }
+ }
+#endif
+}
+
+void MTLBatch::draw_advanced(int v_first, int v_count, int i_first, int i_count)
+{
+
+#if TRUST_NO_ONE
+ BLI_assert(v_count > 0 && i_count > 0);
+#endif
+
+ /* Setup RenderPipelineState for batch. */
+ MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get());
+ id<MTLRenderCommandEncoder> rec = this->bind(v_first, v_count, i_first, i_count);
+ if (rec == nil) {
+ return;
+ }
+
+ /* Fetch IndexBuffer and resolve primitive type. */
+ MTLIndexBuf *mtl_elem = static_cast<MTLIndexBuf *>(reinterpret_cast<IndexBuf *>(this->elem));
+ MTLPrimitiveType mtl_prim_type = gpu_prim_type_to_metal(this->prim_type);
+
+ /* Render using SSBO Vertex Fetch. */
+ if (active_shader_->get_uses_ssbo_vertex_fetch()) {
+
+ /* Submit draw call with modified vertex count, which reflects vertices per primitive defined
+ * in the USE_SSBO_VERTEX_FETCH pragma. */
+ int num_input_primitives = gpu_get_prim_count_from_type(v_count, this->prim_type);
+ int output_num_verts = num_input_primitives *
+ active_shader_->get_ssbo_vertex_fetch_output_num_verts();
+ BLI_assert_msg(
+ mtl_vertex_count_fits_primitive_type(
+ output_num_verts, active_shader_->get_ssbo_vertex_fetch_output_prim_type()),
+ "Output Vertex count is not compatible with the requested output vertex primitive type");
+ [rec drawPrimitives:active_shader_->get_ssbo_vertex_fetch_output_prim_type()
+ vertexStart:0
+ vertexCount:output_num_verts
+ instanceCount:i_count
+ baseInstance:i_first];
+ ctx->main_command_buffer.register_draw_counters(output_num_verts * i_count);
+ }
+ /* Perform regular draw. */
+ else if (mtl_elem == NULL) {
+
+ /* Primitive Type toplogy emulation. */
+ if (mtl_needs_topology_emulation(this->prim_type)) {
+
+ /* Generate index buffer for primitive types requiring emulation. */
+ GPUPrimType emulated_prim_type = this->prim_type;
+ uint32_t emulated_v_count = v_count;
+ id<MTLBuffer> generated_index_buffer = this->get_emulated_toplogy_buffer(emulated_prim_type,
+ emulated_v_count);
+ BLI_assert(generated_index_buffer != nil);
+
+ MTLPrimitiveType emulated_mtl_prim_type = gpu_prim_type_to_metal(emulated_prim_type);
+
+ /* Temp: Disable culling for emulated primitive types.
+ * TODO(Metal): Support face winding in topology buffer. */
+ [rec setCullMode:MTLCullModeNone];
+
+ if (generated_index_buffer != nil) {
+ BLI_assert(emulated_mtl_prim_type == MTLPrimitiveTypeTriangle ||
+ emulated_mtl_prim_type == MTLPrimitiveTypeLine);
+ if (emulated_mtl_prim_type == MTLPrimitiveTypeTriangle) {
+ BLI_assert(emulated_v_count % 3 == 0);
+ }
+ if (emulated_mtl_prim_type == MTLPrimitiveTypeLine) {
+ BLI_assert(emulated_v_count % 2 == 0);
+ }
+
+ /* Set depth stencil state (requires knowledge of primitive type). */
+ ctx->ensure_depth_stencil_state(emulated_mtl_prim_type);
+
+ [rec drawIndexedPrimitives:emulated_mtl_prim_type
+ indexCount:emulated_v_count
+ indexType:MTLIndexTypeUInt32
+ indexBuffer:generated_index_buffer
+ indexBufferOffset:0
+ instanceCount:i_count
+ baseVertex:v_first
+ baseInstance:i_first];
+ }
+ else {
+ printf("[Note] Cannot draw batch -- Emulated Topology mode: %u not yet supported\n",
+ this->prim_type);
+ }
+ }
+ else {
+ /* Set depth stencil state (requires knowledge of primitive type). */
+ ctx->ensure_depth_stencil_state(mtl_prim_type);
+
+ /* Issue draw call. */
+ [rec drawPrimitives:mtl_prim_type
+ vertexStart:v_first
+ vertexCount:v_count
+ instanceCount:i_count
+ baseInstance:i_first];
+ }
+ ctx->main_command_buffer.register_draw_counters(v_count * i_count);
+ }
+ /* Perform indexed draw. */
+ else {
+
+ MTLIndexType index_type = MTLIndexBuf::gpu_index_type_to_metal(mtl_elem->index_type_);
+ uint32_t base_index = mtl_elem->index_base_;
+ uint32_t index_size = (mtl_elem->index_type_ == GPU_INDEX_U16) ? 2 : 4;
+ uint32_t v_first_ofs = ((v_first + mtl_elem->index_start_) * index_size);
+ BLI_assert_msg((v_first_ofs % index_size) == 0,
+ "Index offset is not 2/4-byte aligned as per METAL spec");
+
+ /* Fetch index buffer. May return an index buffer of a differing format,
+ * if index buffer optimization is used. In these cases, final_prim_type and
+ * index_count get updated with the new properties. */
+ GPUPrimType final_prim_type = this->prim_type;
+ uint index_count = v_count;
+
+ id<MTLBuffer> index_buffer = mtl_elem->get_index_buffer(final_prim_type, index_count);
+ mtl_prim_type = gpu_prim_type_to_metal(final_prim_type);
+ BLI_assert(index_buffer != nil);
+
+ if (index_buffer != nil) {
+
+ /* Set depth stencil state (requires knowledge of primitive type). */
+ ctx->ensure_depth_stencil_state(mtl_prim_type);
+
+ /* Issue draw call. */
+ [rec drawIndexedPrimitives:mtl_prim_type
+ indexCount:index_count
+ indexType:index_type
+ indexBuffer:index_buffer
+ indexBufferOffset:v_first_ofs
+ instanceCount:i_count
+ baseVertex:base_index
+ baseInstance:i_first];
+ ctx->main_command_buffer.register_draw_counters(index_count * i_count);
+ }
+ else {
+ BLI_assert_msg(false, "Index buffer does not have backing Metal buffer");
+ }
+ }
+
+ /* End of draw. */
+ this->unbind();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Topology emulation and optimization
+ * \{ */
+
+id<MTLBuffer> MTLBatch::get_emulated_toplogy_buffer(GPUPrimType &in_out_prim_type,
+ uint32_t &in_out_v_count)
+{
+
+ BLI_assert(in_out_v_count > 0);
+ /* Determine emulated primitive types. */
+ GPUPrimType input_prim_type = in_out_prim_type;
+ uint32_t v_count = in_out_v_count;
+ GPUPrimType output_prim_type;
+ switch (input_prim_type) {
+ case GPU_PRIM_POINTS:
+ case GPU_PRIM_LINES:
+ case GPU_PRIM_TRIS:
+ BLI_assert_msg(false, "Optimal primitive types should not reach here.");
+ return nil;
+ break;
+ case GPU_PRIM_LINES_ADJ:
+ case GPU_PRIM_TRIS_ADJ:
+ BLI_assert_msg(false, "Adjacency primitive types should not reach here.");
+ return nil;
+ break;
+ case GPU_PRIM_LINE_STRIP:
+ case GPU_PRIM_LINE_LOOP:
+ case GPU_PRIM_LINE_STRIP_ADJ:
+ output_prim_type = GPU_PRIM_LINES;
+ break;
+ case GPU_PRIM_TRI_STRIP:
+ case GPU_PRIM_TRI_FAN:
+ output_prim_type = GPU_PRIM_TRIS;
+ break;
+ default:
+ BLI_assert_msg(false, "Invalid primitive type.");
+ return nil;
+ }
+
+ /* Check if topology buffer exists and is valid. */
+ if (this->emulated_topology_buffer_ != nullptr &&
+ (emulated_topology_type_ != input_prim_type || topology_buffer_input_v_count_ != v_count)) {
+
+ /* Release existing topology buffer. */
+ emulated_topology_buffer_->free();
+ emulated_topology_buffer_ = nullptr;
+ }
+
+ /* Generate new topology index buffer. */
+ if (this->emulated_topology_buffer_ == nullptr) {
+ /* Calculate IB len. */
+ uint32_t output_prim_count = 0;
+ switch (input_prim_type) {
+ case GPU_PRIM_LINE_STRIP:
+ case GPU_PRIM_LINE_STRIP_ADJ:
+ output_prim_count = v_count - 1;
+ break;
+ case GPU_PRIM_LINE_LOOP:
+ output_prim_count = v_count;
+ break;
+ case GPU_PRIM_TRI_STRIP:
+ case GPU_PRIM_TRI_FAN:
+ output_prim_count = v_count - 2;
+ break;
+ default:
+ BLI_assert_msg(false, "Cannot generate optimized topology buffer for other types.");
+ break;
+ }
+ uint32_t output_IB_elems = output_prim_count * ((output_prim_type == GPU_PRIM_TRIS) ? 3 : 2);
+
+ /* Allocate buffer. */
+ uint32_t buffer_bytes = output_IB_elems * 4;
+ BLI_assert(buffer_bytes > 0);
+ this->emulated_topology_buffer_ = MTLContext::get_global_memory_manager().allocate(
+ buffer_bytes, true);
+
+ /* Populate. */
+ uint32_t *data = (uint32_t *)this->emulated_topology_buffer_->get_host_ptr();
+ BLI_assert(data != nullptr);
+
+ /* TODO(Metal): Support inverse winding modes. */
+ bool winding_clockwise = false;
+ UNUSED_VARS(winding_clockwise);
+
+ switch (input_prim_type) {
+ /* Line Loop. */
+ case GPU_PRIM_LINE_LOOP: {
+ int line = 0;
+ for (line = 0; line < output_prim_count - 1; line++) {
+ data[line * 3 + 0] = line + 0;
+ data[line * 3 + 1] = line + 1;
+ }
+ /* Closing line. */
+ data[line * 2 + 0] = line + 0;
+ data[line * 2 + 1] = 0;
+ } break;
+
+ /* Triangle Fan. */
+ case GPU_PRIM_TRI_FAN: {
+ for (int triangle = 0; triangle < output_prim_count; triangle++) {
+ data[triangle * 3 + 0] = 0; /* Always 0 */
+ data[triangle * 3 + 1] = triangle + 1;
+ data[triangle * 3 + 2] = triangle + 2;
+ }
+ } break;
+
+ default:
+ BLI_assert_msg(false, "Other primitive types do not require emulation.");
+ return nil;
+ }
+
+ /* Flush. */
+ this->emulated_topology_buffer_->flush();
+ /* Assign members relating to current cached IB. */
+ topology_buffer_input_v_count_ = v_count;
+ topology_buffer_output_v_count_ = output_IB_elems;
+ emulated_topology_type_ = input_prim_type;
+ }
+
+ /* Return. */
+ in_out_v_count = topology_buffer_output_v_count_;
+ in_out_prim_type = output_prim_type;
+ return (emulated_topology_buffer_) ? emulated_topology_buffer_->get_metal_buffer() : nil;
+}
+
+/** \} */
+
+} // blender::gpu
diff --git a/source/blender/gpu/metal/mtl_context.mm b/source/blender/gpu/metal/mtl_context.mm
index ef66a1f2111..50576379f0d 100644
--- a/source/blender/gpu/metal/mtl_context.mm
+++ b/source/blender/gpu/metal/mtl_context.mm
@@ -995,19 +995,21 @@ bool MTLContext::ensure_uniform_buffer_bindings(
if (ubo.buffer_index >= 0) {
- const uint32_t buffer_index = ubo.buffer_index;
+ /* Uniform Buffer index offset by 1 as the first shader buffer binding slot is reserved for
+ * the uniform PushConstantBlock. */
+ const uint32_t buffer_index = ubo.buffer_index + 1;
int ubo_offset = 0;
id<MTLBuffer> ubo_buffer = nil;
int ubo_size = 0;
bool bind_dummy_buffer = false;
- if (this->pipeline_state.ubo_bindings[buffer_index].bound) {
+ if (this->pipeline_state.ubo_bindings[ubo_index].bound) {
/* Fetch UBO global-binding properties from slot. */
ubo_offset = 0;
- ubo_buffer = this->pipeline_state.ubo_bindings[buffer_index].ubo->get_metal_buffer(
+ ubo_buffer = this->pipeline_state.ubo_bindings[ubo_index].ubo->get_metal_buffer(
&ubo_offset);
- ubo_size = this->pipeline_state.ubo_bindings[buffer_index].ubo->get_size();
+ ubo_size = this->pipeline_state.ubo_bindings[ubo_index].ubo->get_size();
/* Use dummy zero buffer if no buffer assigned -- this is an optimization to avoid
* allocating zero buffers. */
diff --git a/source/blender/gpu/metal/mtl_drawlist.hh b/source/blender/gpu/metal/mtl_drawlist.hh
index ed99c76faa7..47055f3d7f4 100644
--- a/source/blender/gpu/metal/mtl_drawlist.hh
+++ b/source/blender/gpu/metal/mtl_drawlist.hh
@@ -9,34 +9,50 @@
#pragma once
-#pragma once
-
+#include "BLI_sys_types.h"
+#include "GPU_batch.h"
+#include "MEM_guardedalloc.h"
#include "gpu_drawlist_private.hh"
-namespace blender {
-namespace gpu {
+#include "mtl_batch.hh"
+#include "mtl_context.hh"
+
+namespace blender::gpu {
/**
- * TODO(Metal): MTLDrawList Implementation. Included as temporary stub.
- */
+ * Implementation of Multi Draw Indirect using OpenGL.
+ **/
class MTLDrawList : public DrawList {
+
+ private:
+ /** Batch for which we are recording commands for. */
+ MTLBatch *batch_;
+ /** Mapped memory bounds. */
+ void *data_;
+ /** Length of the mapped buffer (in byte). */
+ size_t data_size_;
+ /** Current offset inside the mapped buffer (in byte). */
+ size_t command_offset_;
+ /** Current number of command recorded inside the mapped buffer. */
+ uint32_t command_len_;
+ /** Is UINT_MAX if not drawing indexed geom. Also Avoid dereferencing batch. */
+ uint32_t base_index_;
+ /** Also Avoid dereferencing batch. */
+ uint32_t v_first_, v_count_;
+ /** Length of whole the buffer (in byte). */
+ uint32_t buffer_size_;
+
public:
- MTLDrawList(int length)
- {
- }
- ~MTLDrawList()
- {
- }
-
- void append(GPUBatch *batch, int i_first, int i_count) override
- {
- }
- void submit() override
- {
- }
+ MTLDrawList(int length);
+ ~MTLDrawList();
+
+ void append(GPUBatch *batch, int i_first, int i_count) override;
+ void submit() override;
+
+ private:
+ void init();
MEM_CXX_CLASS_ALLOC_FUNCS("MTLDrawList");
};
-} // namespace gpu
-} // namespace blender
+} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_drawlist.mm b/source/blender/gpu/metal/mtl_drawlist.mm
new file mode 100644
index 00000000000..99194d2b72c
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_drawlist.mm
@@ -0,0 +1,284 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ *
+ * Implementation of Multi Draw Indirect using OpenGL.
+ * Fallback if the needed extensions are not supported.
+ */
+
+#include "BLI_assert.h"
+
+#include "GPU_batch.h"
+#include "mtl_common.hh"
+#include "mtl_drawlist.hh"
+#include "mtl_primitive.hh"
+
+using namespace blender::gpu;
+
+namespace blender::gpu {
+
+/* Indirect draw call structure for reference. */
+/* MTLDrawPrimitivesIndirectArguments --
+ * https://developer.apple.com/documentation/metal/mtldrawprimitivesindirectarguments?language=objc
+ */
+/* struct MTLDrawPrimitivesIndirectArguments {
+ * uint32_t vertexCount;
+ * uint32_t instanceCount;
+ * uint32_t vertexStart;
+ * uint32_t baseInstance;
+};*/
+
+/* MTLDrawIndexedPrimitivesIndirectArguments --
+ * https://developer.apple.com/documentation/metal/mtldrawindexedprimitivesindirectarguments?language=objc
+ */
+/* struct MTLDrawIndexedPrimitivesIndirectArguments {
+ * uint32_t indexCount;
+ * uint32_t instanceCount;
+ * uint32_t indexStart;
+ * uint32_t baseVertex;
+ * uint32_t baseInstance;
+};*/
+
+#define MDI_ENABLED (buffer_size_ != 0)
+#define MDI_DISABLED (buffer_size_ == 0)
+#define MDI_INDEXED (base_index_ != UINT_MAX)
+
+MTLDrawList::MTLDrawList(int length)
+{
+ BLI_assert(length > 0);
+ batch_ = nullptr;
+ command_len_ = 0;
+ base_index_ = 0;
+ command_offset_ = 0;
+ data_size_ = 0;
+ buffer_size_ = sizeof(MTLDrawIndexedPrimitivesIndirectArguments) * length;
+ data_ = (void *)MEM_mallocN(buffer_size_, __func__);
+}
+
+MTLDrawList::~MTLDrawList()
+{
+ if (data_) {
+ MEM_freeN(data_);
+ data_ = nullptr;
+ }
+}
+
+void MTLDrawList::init()
+{
+ MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get());
+ BLI_assert(ctx);
+ BLI_assert(MDI_ENABLED);
+ BLI_assert(data_ == nullptr);
+ UNUSED_VARS_NDEBUG(ctx);
+
+ batch_ = nullptr;
+ command_len_ = 0;
+ BLI_assert(data_);
+
+ command_offset_ = 0;
+}
+
+void MTLDrawList::append(GPUBatch *gpu_batch, int i_first, int i_count)
+{
+ /* Fallback when MultiDrawIndirect is not supported/enabled. */
+ MTLShader *shader = static_cast<MTLShader *>(unwrap(gpu_batch->shader));
+ bool requires_ssbo = (shader->get_uses_ssbo_vertex_fetch());
+ bool requires_emulation = mtl_needs_topology_emulation(gpu_batch->prim_type);
+ if (MDI_DISABLED || requires_ssbo || requires_emulation) {
+ GPU_batch_draw_advanced(gpu_batch, 0, 0, i_first, i_count);
+ return;
+ }
+
+ if (data_ == nullptr) {
+ this->init();
+ }
+ BLI_assert(data_);
+
+ MTLBatch *mtl_batch = static_cast<MTLBatch *>(gpu_batch);
+ BLI_assert(mtl_batch);
+ if (mtl_batch != batch_) {
+ /* Submit existing calls. */
+ this->submit();
+
+ /* Begin new batch. */
+ batch_ = mtl_batch;
+
+ /* Cached for faster access. */
+ MTLIndexBuf *el = batch_->elem_();
+ base_index_ = el ? el->index_base_ : UINT_MAX;
+ v_first_ = el ? el->index_start_ : 0;
+ v_count_ = el ? el->index_len_ : batch_->verts_(0)->vertex_len;
+ }
+
+ if (v_count_ == 0) {
+ /* Nothing to draw. */
+ return;
+ }
+
+ if (MDI_INDEXED) {
+ MTLDrawIndexedPrimitivesIndirectArguments *cmd =
+ reinterpret_cast<MTLDrawIndexedPrimitivesIndirectArguments *>((char *)data_ +
+ command_offset_);
+ cmd->indexStart = v_first_;
+ cmd->indexCount = v_count_;
+ cmd->instanceCount = i_count;
+ cmd->baseVertex = base_index_;
+ cmd->baseInstance = i_first;
+ }
+ else {
+ MTLDrawPrimitivesIndirectArguments *cmd =
+ reinterpret_cast<MTLDrawPrimitivesIndirectArguments *>((char *)data_ + command_offset_);
+ cmd->vertexStart = v_first_;
+ cmd->vertexCount = v_count_;
+ cmd->instanceCount = i_count;
+ cmd->baseInstance = i_first;
+ }
+
+ size_t command_size = MDI_INDEXED ? sizeof(MTLDrawIndexedPrimitivesIndirectArguments) :
+ sizeof(MTLDrawPrimitivesIndirectArguments);
+
+ command_offset_ += command_size;
+ command_len_++;
+
+ /* Check if we can fit at least one other command. */
+ if (command_offset_ + command_size > buffer_size_) {
+ this->submit();
+ }
+
+ return;
+}
+
+void MTLDrawList::submit()
+{
+ /* Metal does not support MDI from the host side, but we still benefit from only executing the
+ * batch bind a single time, rather than per-draw.
+ * NOTE(Metal): Consider using #MTLIndirectCommandBuffer to achieve similar behavior. */
+ if (command_len_ == 0) {
+ return;
+ }
+
+ /* Something's wrong if we get here without MDI support. */
+ BLI_assert(MDI_ENABLED);
+ BLI_assert(data_);
+
+ /* Host-side MDI Currently unsupported on Metal. */
+ bool can_use_MDI = false;
+
+ /* Verify context. */
+ MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get());
+ BLI_assert(ctx);
+
+ /* Execute indirect draw calls. */
+ MTLShader *shader = static_cast<MTLShader *>(unwrap(batch_->shader));
+ bool SSBO_MODE = (shader->get_uses_ssbo_vertex_fetch());
+ if (SSBO_MODE) {
+ can_use_MDI = false;
+ BLI_assert(false);
+ return;
+ }
+
+ /* Heuristic to determine whether using indirect drawing is more efficient. */
+ size_t command_size = MDI_INDEXED ? sizeof(MTLDrawIndexedPrimitivesIndirectArguments) :
+ sizeof(MTLDrawPrimitivesIndirectArguments);
+ const bool is_finishing_a_buffer = (command_offset_ + command_size > buffer_size_);
+ can_use_MDI = can_use_MDI && (is_finishing_a_buffer || command_len_ > 2);
+
+ /* Bind Batch to setup render pipeline state. */
+ id<MTLRenderCommandEncoder> rec = batch_->bind(0, 0, 0, 0);
+ if (!rec) {
+ BLI_assert_msg(false, "A RenderCommandEncoder should always be available!\n");
+ return;
+ }
+
+ /* Common properties. */
+ MTLPrimitiveType mtl_prim_type = gpu_prim_type_to_metal(batch_->prim_type);
+
+ /* Execute multi-draw indirect. */
+ if (can_use_MDI && false) {
+ /* Metal Doesn't support MDI -- Singular Indirect draw calls are supported,
+ * but Multi-draw is not.
+ * TODO(Metal): Consider using #IndirectCommandBuffers to provide similar
+ * behavior. */
+ }
+ else {
+
+ /* Execute draws manually. */
+ if (MDI_INDEXED) {
+ MTLDrawIndexedPrimitivesIndirectArguments *cmd =
+ (MTLDrawIndexedPrimitivesIndirectArguments *)data_;
+ MTLIndexBuf *mtl_elem = static_cast<MTLIndexBuf *>(
+ reinterpret_cast<IndexBuf *>(batch_->elem));
+ BLI_assert(mtl_elem);
+ MTLIndexType index_type = MTLIndexBuf::gpu_index_type_to_metal(mtl_elem->index_type_);
+ uint32_t index_size = (mtl_elem->index_type_ == GPU_INDEX_U16) ? 2 : 4;
+ uint32_t v_first_ofs = (mtl_elem->index_start_ * index_size);
+ uint32_t index_count = cmd->indexCount;
+
+ /* Fetch index buffer. May return an index buffer of a differing format,
+ * if index buffer optimization is used. In these cases, mtl_prim_type and
+ * index_count get updated with the new properties. */
+ GPUPrimType final_prim_type = batch_->prim_type;
+ id<MTLBuffer> index_buffer = mtl_elem->get_index_buffer(final_prim_type, index_count);
+ BLI_assert(index_buffer != nil);
+
+ /* Final primitive type. */
+ mtl_prim_type = gpu_prim_type_to_metal(final_prim_type);
+
+ if (index_buffer != nil) {
+
+ /* Set depth stencil state (requires knowledge of primitive type). */
+ ctx->ensure_depth_stencil_state(mtl_prim_type);
+
+ for (int i = 0; i < command_len_; i++, cmd++) {
+ [rec drawIndexedPrimitives:mtl_prim_type
+ indexCount:index_count
+ indexType:index_type
+ indexBuffer:index_buffer
+ indexBufferOffset:v_first_ofs
+ instanceCount:cmd->instanceCount
+ baseVertex:cmd->baseVertex
+ baseInstance:cmd->baseInstance];
+ ctx->main_command_buffer.register_draw_counters(cmd->indexCount * cmd->instanceCount);
+ }
+ }
+ else {
+ BLI_assert_msg(false, "Index buffer does not have backing Metal buffer");
+ }
+ }
+ else {
+ MTLDrawPrimitivesIndirectArguments *cmd = (MTLDrawPrimitivesIndirectArguments *)data_;
+
+ /* Verify if topology emulation is required. */
+ if (mtl_needs_topology_emulation(batch_->prim_type)) {
+ BLI_assert_msg(false, "topology emulation cases should use fallback.");
+ }
+ else {
+
+ /* Set depth stencil state (requires knowledge of primitive type). */
+ ctx->ensure_depth_stencil_state(mtl_prim_type);
+
+ for (int i = 0; i < command_len_; i++, cmd++) {
+ [rec drawPrimitives:mtl_prim_type
+ vertexStart:cmd->vertexStart
+ vertexCount:cmd->vertexCount
+ instanceCount:cmd->instanceCount
+ baseInstance:cmd->baseInstance];
+ ctx->main_command_buffer.register_draw_counters(cmd->vertexCount * cmd->instanceCount);
+ }
+ }
+ }
+ }
+
+ /* Unbind batch. */
+ batch_->unbind();
+
+ /* Reset command offsets. */
+ command_len_ = 0;
+ command_offset_ = 0;
+
+ /* Avoid keeping reference to the batch. */
+ batch_ = nullptr;
+}
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_immediate.mm b/source/blender/gpu/metal/mtl_immediate.mm
index 4b63a3b1ce2..ee48bdd6ee1 100644
--- a/source/blender/gpu/metal/mtl_immediate.mm
+++ b/source/blender/gpu/metal/mtl_immediate.mm
@@ -99,6 +99,9 @@ void MTLImmediate::end()
MTLRenderPipelineStateDescriptor &desc = state_manager->get_pipeline_descriptor();
const MTLShaderInterface *interface = active_mtl_shader->get_interface();
+ /* Reset vertex descriptor to default state. */
+ desc.reset_vertex_descriptor();
+
desc.vertex_descriptor.num_attributes = interface->get_total_attributes();
desc.vertex_descriptor.num_vert_buffers = 1;
diff --git a/source/blender/gpu/metal/mtl_pso_descriptor_state.hh b/source/blender/gpu/metal/mtl_pso_descriptor_state.hh
index 198d309874b..04ceb5bdf03 100644
--- a/source/blender/gpu/metal/mtl_pso_descriptor_state.hh
+++ b/source/blender/gpu/metal/mtl_pso_descriptor_state.hh
@@ -243,6 +243,19 @@ struct MTLRenderPipelineStateDescriptor {
return hash;
}
+
+ /* Reset the Vertex Descriptor to default. */
+ void reset_vertex_descriptor()
+ {
+ vertex_descriptor.num_attributes = 0;
+ vertex_descriptor.num_vert_buffers = 0;
+ for (int i = 0; i < GPU_VERT_ATTR_MAX_LEN; i++) {
+ vertex_descriptor.attributes[i].format = MTLVertexFormatInvalid;
+ vertex_descriptor.attributes[i].offset = 0;
+ }
+ vertex_descriptor.uses_ssbo_vertex_fetch = false;
+ vertex_descriptor.num_ssbo_attributes = 0;
+ }
};
} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_shader_generator.hh b/source/blender/gpu/metal/mtl_shader_generator.hh
index 43890ca0170..63e2e6d5924 100644
--- a/source/blender/gpu/metal/mtl_shader_generator.hh
+++ b/source/blender/gpu/metal/mtl_shader_generator.hh
@@ -497,7 +497,7 @@ inline std::string get_stage_class_name(ShaderStage stage)
inline bool is_builtin_type(std::string type)
{
/* Add Types as needed. */
- /* TODO(Metal): Consider replacing this with a switch and constexpr hash and switch.
+ /* TODO(Metal): Consider replacing this with a switch and `constexpr` hash and switch.
* Though most efficient and maintainable approach to be determined. */
static std::map<std::string, eMTLDataType> glsl_builtin_types = {
{"float", MTL_DATATYPE_FLOAT},
diff --git a/source/blender/gpu/metal/mtl_shader_interface.mm b/source/blender/gpu/metal/mtl_shader_interface.mm
index 3703d5b5684..97a82345761 100644
--- a/source/blender/gpu/metal/mtl_shader_interface.mm
+++ b/source/blender/gpu/metal/mtl_shader_interface.mm
@@ -117,9 +117,7 @@ uint32_t MTLShaderInterface::add_uniform_block(uint32_t name_offset,
MTLShaderUniformBlock &uni_block = ubos_[total_uniform_blocks_];
uni_block.name_offset = name_offset;
- /* We offset the buffer binding index by one, as the first slot is reserved for push constant
- * data. */
- uni_block.buffer_index = buffer_index + 1;
+ uni_block.buffer_index = buffer_index;
uni_block.size = size;
uni_block.current_offset = 0;
uni_block.stage_mask = ShaderStage::BOTH;
@@ -297,8 +295,10 @@ void MTLShaderInterface::prepare_common_shader_inputs()
current_input->name_hash = BLI_hash_string(this->get_name_at_offset(shd_ubo.name_offset));
/* Location refers to the index in the ubos_ array. */
current_input->location = ubo_index;
- /* Final binding location refers to the buffer binding index within the shader (Relative to
- * MTL_uniform_buffer_base_index). */
+ /* Binding location refers to the UBO bind slot in
+ * #MTLContextGlobalShaderPipelineState::ubo_bindings. The buffer bind index [[buffer(N)]]
+ * within the shader will apply an offset for bound vertex buffers and the default uniform
+ * PushConstantBlock. */
current_input->binding = shd_ubo.buffer_index;
current_input++;
}
diff --git a/source/blender/gpu/metal/mtl_texture.hh b/source/blender/gpu/metal/mtl_texture.hh
index ebc9eb2e00e..28b55306707 100644
--- a/source/blender/gpu/metal/mtl_texture.hh
+++ b/source/blender/gpu/metal/mtl_texture.hh
@@ -51,9 +51,9 @@ struct TextureUpdateRoutineSpecialisation {
uint64_t hash() const
{
blender::DefaultHash<std::string> string_hasher;
- return uint64_t(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)));
+ std::to_string((this->component_count_input << 8) + this->component_count_output));
}
};
diff --git a/source/blender/gpu/metal/mtl_texture.mm b/source/blender/gpu/metal/mtl_texture.mm
index 32029db6fd9..29dcc8d32ee 100644
--- a/source/blender/gpu/metal/mtl_texture.mm
+++ b/source/blender/gpu/metal/mtl_texture.mm
@@ -337,20 +337,6 @@ void gpu::MTLTexture::blit(gpu::MTLTexture *dst,
GPU_batch_draw(quad);
- /* TMP draw with IMM TODO(Metal): Remove this once GPUBatch is supported. */
- GPUVertFormat *imm_format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(imm_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
-
- immBindShader(shader);
- immBegin(GPU_PRIM_TRI_STRIP, 4);
- immVertex2f(pos, 1, 0);
- immVertex2f(pos, 0, 0);
- immVertex2f(pos, 1, 1);
- immVertex2f(pos, 0, 1);
- immEnd();
- immUnbindProgram();
- /**********************/
-
/* restoring old pipeline state. */
GPU_depth_mask(depth_write_prev);
GPU_stencil_write_mask_set(stencil_mask_prev);
@@ -1472,10 +1458,82 @@ bool gpu::MTLTexture::init_internal()
bool gpu::MTLTexture::init_internal(GPUVertBuf *vbo)
{
- /* Not a valid vertex buffer format, though verifying texture is not set as such
- * as this is not supported on Apple Silicon. */
- BLI_assert_msg(this->format_ != GPU_DEPTH24_STENCIL8,
- "Apple silicon does not support GPU_DEPTH24_S8");
+ if (this->format_ == GPU_DEPTH24_STENCIL8) {
+ /* Apple Silicon requires GPU_DEPTH32F_STENCIL8 instead of GPU_DEPTH24_STENCIL8. */
+ this->format_ = GPU_DEPTH32F_STENCIL8;
+ }
+
+ MTLPixelFormat mtl_format = gpu_texture_format_to_metal(this->format_);
+ mtl_max_mips_ = 1;
+ mipmaps_ = 0;
+ this->mip_range_set(0, 0);
+
+ /* Create texture from GPUVertBuf's buffer. */
+ MTLVertBuf *mtl_vbo = static_cast<MTLVertBuf *>(unwrap(vbo));
+ mtl_vbo->bind();
+ mtl_vbo->flag_used();
+
+ /* Get Metal Buffer. */
+ id<MTLBuffer> source_buffer = mtl_vbo->get_metal_buffer();
+ BLI_assert(source_buffer);
+
+ /* Verify size. */
+ if (w_ <= 0) {
+ MTL_LOG_WARNING("Allocating texture buffer of width 0!\n");
+ w_ = 1;
+ }
+
+ /* Verify Texture and vertex buffer alignment. */
+ int bytes_per_pixel = get_mtl_format_bytesize(mtl_format);
+ int bytes_per_row = bytes_per_pixel * w_;
+
+ MTLContext *mtl_ctx = MTLContext::get();
+ uint32_t align_requirement = static_cast<uint32_t>(
+ [mtl_ctx->device minimumLinearTextureAlignmentForPixelFormat:mtl_format]);
+
+ /* Verify per-vertex size aligns with texture size. */
+ const GPUVertFormat *format = GPU_vertbuf_get_format(vbo);
+ BLI_assert(bytes_per_pixel == format->stride &&
+ "Pixel format stride MUST match the texture format stride -- These being different "
+ "is likely caused by Metal's VBO padding to a minimum of 4-bytes per-vertex");
+ UNUSED_VARS_NDEBUG(format);
+
+ /* Create texture descriptor. */
+ BLI_assert(type_ == GPU_TEXTURE_BUFFER);
+ texture_descriptor_ = [[MTLTextureDescriptor alloc] init];
+ texture_descriptor_.pixelFormat = mtl_format;
+ texture_descriptor_.textureType = MTLTextureTypeTextureBuffer;
+ texture_descriptor_.width = w_;
+ texture_descriptor_.height = 1;
+ texture_descriptor_.depth = 1;
+ texture_descriptor_.arrayLength = 1;
+ texture_descriptor_.mipmapLevelCount = mtl_max_mips_;
+ texture_descriptor_.usage =
+ MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite |
+ MTLTextureUsagePixelFormatView; /* TODO(Metal): Optimize usage flags. */
+ texture_descriptor_.storageMode = [source_buffer storageMode];
+ texture_descriptor_.sampleCount = 1;
+ texture_descriptor_.cpuCacheMode = [source_buffer cpuCacheMode];
+ texture_descriptor_.hazardTrackingMode = [source_buffer hazardTrackingMode];
+
+ texture_ = [source_buffer
+ newTextureWithDescriptor:texture_descriptor_
+ offset:0
+ bytesPerRow:ceil_to_multiple_u(bytes_per_row, align_requirement)];
+ aligned_w_ = bytes_per_row / bytes_per_pixel;
+
+ BLI_assert(texture_);
+ texture_.label = [NSString stringWithUTF8String:this->get_name()];
+ is_baked_ = true;
+ is_dirty_ = false;
+ resource_mode_ = MTL_TEXTURE_MODE_VBO;
+
+ /* Track Status. */
+ vert_buffer_ = mtl_vbo;
+ vert_buffer_mtl_ = source_buffer;
+ /* Cleanup. */
+ [texture_descriptor_ release];
+ texture_descriptor_ = nullptr;
return true;
}
@@ -1522,7 +1580,6 @@ bool gpu::MTLTexture::texture_is_baked()
/* Prepare texture parameters after initialization, but before baking. */
void gpu::MTLTexture::prepare_internal()
{
-
/* Derive implicit usage flags for Depth/Stencil attachments. */
if (format_flag_ & GPU_FORMAT_DEPTH || format_flag_ & GPU_FORMAT_STENCIL) {
gpu_image_usage_flags_ |= GPU_TEXTURE_USAGE_ATTACHMENT;
@@ -1687,7 +1744,7 @@ void gpu::MTLTexture::ensure_baked()
/* Determine Resource Mode. */
resource_mode_ = MTL_TEXTURE_MODE_DEFAULT;
- /* Create texture. */
+ /* Standard texture allocation. */
texture_ = [ctx->device newTextureWithDescriptor:texture_descriptor_];
[texture_descriptor_ release];
diff --git a/source/blender/gpu/metal/mtl_texture_util.mm b/source/blender/gpu/metal/mtl_texture_util.mm
index 5ed7659f260..33a62e2e3ef 100644
--- a/source/blender/gpu/metal/mtl_texture_util.mm
+++ b/source/blender/gpu/metal/mtl_texture_util.mm
@@ -34,7 +34,7 @@ MTLPixelFormat gpu_texture_format_to_metal(eGPUTextureFormat tex_format)
{
switch (tex_format) {
- /* Formats texture & renderbuffer. */
+ /* Formats texture & render-buffer. */
case GPU_RGBA8UI:
return MTLPixelFormatRGBA8Uint;
case GPU_RGBA8I:
diff --git a/source/blender/gpu/opengl/gl_batch.cc b/source/blender/gpu/opengl/gl_batch.cc
index ff8867fe3e6..28105e326ee 100644
--- a/source/blender/gpu/opengl/gl_batch.cc
+++ b/source/blender/gpu/opengl/gl_batch.cc
@@ -272,8 +272,8 @@ void GLBatch::bind(int i_first)
#if GPU_TRACK_INDEX_RANGE
/* Can be removed if GL 4.3 is required. */
- if (!GLContext::fixed_restart_index_support && (elem != nullptr)) {
- glPrimitiveRestartIndex(this->elem_()->restart_index());
+ if (!GLContext::fixed_restart_index_support) {
+ glPrimitiveRestartIndex((elem != nullptr) ? this->elem_()->restart_index() : 0xFFFFFFFFu);
}
#endif
diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc
index c9432fca561..ef97d74bf81 100644
--- a/source/blender/gpu/opengl/gl_shader_interface.cc
+++ b/source/blender/gpu/opengl/gl_shader_interface.cc
@@ -200,6 +200,9 @@ static Type gpu_type_from_gl_type(int gl_type)
GLShaderInterface::GLShaderInterface(GLuint program)
{
+ GLuint last_program;
+ glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *)&last_program);
+
/* Necessary to make #glUniform works. */
glUseProgram(program);
@@ -385,6 +388,8 @@ GLShaderInterface::GLShaderInterface(GLuint program)
// this->debug_print();
this->sort_inputs();
+
+ glUseProgram(last_program);
}
GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateInfo &info)
@@ -442,6 +447,9 @@ GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateI
uint32_t name_buffer_offset = 0;
/* Necessary to make #glUniform works. TODO(fclem) Remove. */
+ GLuint last_program;
+ glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *)&last_program);
+
glUseProgram(program);
/* Attributes */
@@ -552,6 +560,8 @@ GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateI
this->sort_inputs();
// this->debug_print();
+
+ glUseProgram(last_program);
}
GLShaderInterface::~GLShaderInterface()
diff --git a/source/blender/gpu/shaders/compositor/compositor_blur.glsl b/source/blender/gpu/shaders/compositor/compositor_blur.glsl
index 4f981c84f59..c7ac620f99b 100644
--- a/source/blender/gpu/shaders/compositor/compositor_blur.glsl
+++ b/source/blender/gpu/shaders/compositor/compositor_blur.glsl
@@ -18,13 +18,24 @@ vec4 load_input(ivec2 texel)
}
/* Given the texel in the range [-radius, radius] in both axis, load the appropriate weight from
- * the weights texture, where the texel (0, 0) is considered the center of weights texture. */
+ * the weights texture, where the given texel (0, 0) corresponds the center of weights texture.
+ * Note that we load the weights texture inverted along both directions to maintain the shape of
+ * the weights if it was not symmetrical. To understand why inversion makes sense, consider a 1D
+ * weights texture whose right half is all ones and whose left half is all zeros. Further, consider
+ * that we are blurring a single white pixel on a black background. When computing the value of a
+ * pixel that is to the right of the white pixel, the white pixel will be in the left region of the
+ * search window, and consequently, without inversion, a zero will be sampled from the left side of
+ * the weights texture and result will be zero. However, what we expect is that pixels to the right
+ * of the white pixel will be white, that is, they should sample a weight of 1 from the right side
+ * of the weights texture, hence the need for inversion. */
vec4 load_weight(ivec2 texel)
{
- /* Add the radius to transform the texel into the range [0, radius * 2], then divide by the upper
- * bound plus one to transform the texel into the normalized range [0, 1] needed to sample the
- * weights sampler. Finally, also add 0.5 to sample at the center of the pixels. */
- return texture(weights_tx, (texel + vec2(radius + 0.5)) / (radius * 2 + 1));
+ /* Add the radius to transform the texel into the range [0, radius * 2], with an additional 0.5
+ * to sample at the center of the pixels, then divide by the upper bound plus one to transform
+ * the texel into the normalized range [0, 1] needed to sample the weights sampler. Finally,
+ * invert the textures coordinates by subtracting from 1 to maintain the shape of the weights as
+ * mentioned in the function description. */
+ return texture(weights_tx, 1.0 - ((texel + vec2(radius + 0.5)) / (radius * 2 + 1)));
}
void main()
diff --git a/source/blender/gpu/shaders/compositor/compositor_blur_variable_size.glsl b/source/blender/gpu/shaders/compositor/compositor_blur_variable_size.glsl
index e7e5aac12a5..9383bbf9825 100644
--- a/source/blender/gpu/shaders/compositor/compositor_blur_variable_size.glsl
+++ b/source/blender/gpu/shaders/compositor/compositor_blur_variable_size.glsl
@@ -2,7 +2,16 @@
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
/* Given the texel in the range [-radius, radius] in both axis, load the appropriate weight from
- * the weights texture, where the texel (0, 0) is considered the center of weights texture. */
+ * the weights texture, where the given texel (0, 0) corresponds the center of weights texture.
+ * Note that we load the weights texture inverted along both directions to maintain the shape of
+ * the weights if it was not symmetrical. To understand why inversion makes sense, consider a 1D
+ * weights texture whose right half is all ones and whose left half is all zeros. Further, consider
+ * that we are blurring a single white pixel on a black background. When computing the value of a
+ * pixel that is to the right of the white pixel, the white pixel will be in the left region of the
+ * search window, and consequently, without inversion, a zero will be sampled from the left side of
+ * the weights texture and result will be zero. However, what we expect is that pixels to the right
+ * of the white pixel will be white, that is, they should sample a weight of 1 from the right side
+ * of the weights texture, hence the need for inversion. */
vec4 load_weight(ivec2 texel, float radius)
{
/* The center zero texel is always assigned a unit weight regardless of the corresponding weight
@@ -12,10 +21,12 @@ vec4 load_weight(ivec2 texel, float radius)
return vec4(1.0);
}
- /* Add the radius to transform the texel into the range [0, radius * 2], then divide by the upper
- * bound plus one to transform the texel into the normalized range [0, 1] needed to sample the
- * weights sampler. Finally, also add 0.5 to sample at the center of the pixels. */
- return texture(weights_tx, (texel + vec2(radius + 0.5)) / (radius * 2 + 1));
+ /* Add the radius to transform the texel into the range [0, radius * 2], with an additional 0.5
+ * to sample at the center of the pixels, then divide by the upper bound plus one to transform
+ * the texel into the normalized range [0, 1] needed to sample the weights sampler. Finally,
+ * invert the textures coordinates by subtracting from 1 to maintain the shape of the weights as
+ * mentioned in the function description. */
+ return texture(weights_tx, 1.0 - ((texel + vec2(radius + 0.5)) / (radius * 2 + 1)));
}
void main()
diff --git a/source/blender/gpu/shaders/compositor/compositor_normalize.glsl b/source/blender/gpu/shaders/compositor/compositor_normalize.glsl
new file mode 100644
index 00000000000..53dfeb01730
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_normalize.glsl
@@ -0,0 +1,10 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ float value = texture_load(input_tx, texel).x;
+ float normalized_value = (value - minimum) * scale;
+ float clamped_value = clamp(normalized_value, 0.0, 1.0);
+ imageStore(output_img, texel, vec4(clamped_value));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_tone_map_photoreceptor.glsl b/source/blender/gpu/shaders/compositor/compositor_tone_map_photoreceptor.glsl
new file mode 100644
index 00000000000..167006585ca
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_tone_map_photoreceptor.glsl
@@ -0,0 +1,22 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+/* Tone mapping based on equation (1) and the trilinear interpolation between equations (6) and (7)
+ * from Reinhard, Erik, and Kate Devlin. "Dynamic range reduction inspired by photoreceptor
+ * physiology." IEEE transactions on visualization and computer graphics 11.1 (2005): 13-24. */
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ vec4 input_color = texture_load(input_tx, texel);
+ float input_luminance = dot(input_color.rgb, luminance_coefficients);
+
+ /* Trilinear interpolation between equations (6) and (7) from Reinhard's 2005 paper. */
+ vec4 local_adaptation_level = mix(vec4(input_luminance), input_color, chromatic_adaptation);
+ vec4 adaptation_level = mix(global_adaptation_level, local_adaptation_level, light_adaptation);
+
+ /* Equation (1) from Reinhard's 2005 paper, assuming Vmax is 1. */
+ vec4 semi_saturation = pow(intensity * adaptation_level, vec4(contrast));
+ vec4 tone_mapped_color = input_color / (input_color + semi_saturation);
+
+ imageStore(output_img, texel, vec4(tone_mapped_color.rgb, input_color.a));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_tone_map_simple.glsl b/source/blender/gpu/shaders/compositor/compositor_tone_map_simple.glsl
new file mode 100644
index 00000000000..ce42d021dd1
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_tone_map_simple.glsl
@@ -0,0 +1,26 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
+
+/* Tone mapping based on equation (3) from Reinhard, Erik, et al. "Photographic tone reproduction
+ * for digital images." Proceedings of the 29th annual conference on Computer graphics and
+ * interactive techniques. 2002. */
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ vec4 input_color = texture_load(input_tx, texel);
+
+ /* Equation (2) from Reinhard's 2002 paper. */
+ vec4 scaled_color = input_color * luminance_scale;
+
+ /* Equation (3) from Reinhard's 2002 paper, but with the 1 replaced with the blend factor for
+ * more flexibility. See ToneMapOperation::compute_luminance_scale_blend_factor. */
+ vec4 denominator = luminance_scale_blend_factor + scaled_color;
+ vec4 tone_mapped_color = safe_divide(scaled_color, denominator);
+
+ if (inverse_gamma != 0.0) {
+ tone_mapped_color = pow(max(tone_mapped_color, vec4(0.0)), vec4(inverse_gamma));
+ }
+
+ imageStore(output_img, texel, vec4(tone_mapped_color.rgb, input_color.a));
+}
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_normalize_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_normalize_info.hh
new file mode 100644
index 00000000000..02fdc424014
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_normalize_info.hh
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_normalize)
+ .local_group_size(16, 16)
+ .push_constant(Type::FLOAT, "minimum")
+ .push_constant(Type::FLOAT, "scale")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_normalize.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_parallel_reduction_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_parallel_reduction_info.hh
index 2e661f280af..e2252b14758 100644
--- a/source/blender/gpu/shaders/compositor/infos/compositor_parallel_reduction_info.hh
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_parallel_reduction_info.hh
@@ -12,14 +12,17 @@ GPU_SHADER_CREATE_INFO(compositor_parallel_reduction_shared)
* Sum Reductions.
*/
-GPU_SHADER_CREATE_INFO(compositor_sum_float_shared)
+GPU_SHADER_CREATE_INFO(compositor_sum_shared)
.additional_info("compositor_parallel_reduction_shared")
- .image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
- .define("TYPE", "float")
.define("IDENTITY", "vec4(0.0)")
- .define("LOAD(value)", "value.x")
.define("REDUCE(lhs, rhs)", "lhs + rhs");
+GPU_SHADER_CREATE_INFO(compositor_sum_float_shared)
+ .additional_info("compositor_sum_shared")
+ .image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("TYPE", "float")
+ .define("LOAD(value)", "value.x");
+
GPU_SHADER_CREATE_INFO(compositor_sum_red)
.additional_info("compositor_sum_float_shared")
.define("INITIALIZE(value)", "value.r")
@@ -41,6 +44,20 @@ GPU_SHADER_CREATE_INFO(compositor_sum_luminance)
.define("INITIALIZE(value)", "dot(value.rgb, luminance_coefficients)")
.do_static_compilation(true);
+GPU_SHADER_CREATE_INFO(compositor_sum_log_luminance)
+ .additional_info("compositor_sum_float_shared")
+ .push_constant(Type::VEC3, "luminance_coefficients")
+ .define("INITIALIZE(value)", "log(max(dot(value.rgb, luminance_coefficients), 1e-5))")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_sum_color)
+ .additional_info("compositor_sum_shared")
+ .image(0, GPU_RGBA32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("TYPE", "vec4")
+ .define("INITIALIZE(value)", "value")
+ .define("LOAD(value)", "value")
+ .do_static_compilation(true);
+
/* --------------------------------------------------------------------
* Sum Of Squared Difference Reductions.
*/
@@ -74,3 +91,59 @@ GPU_SHADER_CREATE_INFO(compositor_sum_luminance_squared_difference)
.push_constant(Type::VEC3, "luminance_coefficients")
.define("INITIALIZE(value)", "pow(dot(value.rgb, luminance_coefficients) - subtrahend, 2.0)")
.do_static_compilation(true);
+
+/* --------------------------------------------------------------------
+ * Maximum Reductions.
+ */
+
+GPU_SHADER_CREATE_INFO(compositor_maximum_luminance)
+ .additional_info("compositor_parallel_reduction_shared")
+ .typedef_source("common_math_lib.glsl")
+ .image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .push_constant(Type::VEC3, "luminance_coefficients")
+ .define("TYPE", "float")
+ .define("IDENTITY", "vec4(FLT_MIN)")
+ .define("INITIALIZE(value)", "dot(value.rgb, luminance_coefficients)")
+ .define("LOAD(value)", "value.x")
+ .define("REDUCE(lhs, rhs)", "max(lhs, rhs)")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_maximum_float_in_range)
+ .additional_info("compositor_parallel_reduction_shared")
+ .image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .push_constant(Type::FLOAT, "lower_bound")
+ .push_constant(Type::FLOAT, "upper_bound")
+ .define("TYPE", "float")
+ .define("IDENTITY", "vec4(lower_bound)")
+ .define("INITIALIZE(v)", "((v.x <= upper_bound) && (v.x >= lower_bound)) ? v.x : lower_bound")
+ .define("LOAD(value)", "value.x")
+ .define("REDUCE(lhs, rhs)", "((rhs > lhs) && (rhs <= upper_bound)) ? rhs : lhs")
+ .do_static_compilation(true);
+
+/* --------------------------------------------------------------------
+ * Minimum Reductions.
+ */
+
+GPU_SHADER_CREATE_INFO(compositor_minimum_luminance)
+ .additional_info("compositor_parallel_reduction_shared")
+ .typedef_source("common_math_lib.glsl")
+ .image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .push_constant(Type::VEC3, "luminance_coefficients")
+ .define("TYPE", "float")
+ .define("IDENTITY", "vec4(FLT_MAX)")
+ .define("INITIALIZE(value)", "dot(value.rgb, luminance_coefficients)")
+ .define("LOAD(value)", "value.x")
+ .define("REDUCE(lhs, rhs)", "min(lhs, rhs)")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_minimum_float_in_range)
+ .additional_info("compositor_parallel_reduction_shared")
+ .image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .push_constant(Type::FLOAT, "lower_bound")
+ .push_constant(Type::FLOAT, "upper_bound")
+ .define("TYPE", "float")
+ .define("IDENTITY", "vec4(upper_bound)")
+ .define("INITIALIZE(v)", "((v.x <= upper_bound) && (v.x >= lower_bound)) ? v.x : upper_bound")
+ .define("LOAD(value)", "value.x")
+ .define("REDUCE(lhs, rhs)", "((rhs < lhs) && (rhs >= lower_bound)) ? rhs : lhs")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_tone_map_photoreceptor_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_tone_map_photoreceptor_info.hh
new file mode 100644
index 00000000000..a460c9d58a6
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_tone_map_photoreceptor_info.hh
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_tone_map_photoreceptor)
+ .local_group_size(16, 16)
+ .push_constant(Type::VEC4, "global_adaptation_level")
+ .push_constant(Type::FLOAT, "contrast")
+ .push_constant(Type::FLOAT, "intensity")
+ .push_constant(Type::FLOAT, "chromatic_adaptation")
+ .push_constant(Type::FLOAT, "light_adaptation")
+ .push_constant(Type::VEC3, "luminance_coefficients")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_tone_map_photoreceptor.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_tone_map_simple_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_tone_map_simple_info.hh
new file mode 100644
index 00000000000..2b220af9460
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_tone_map_simple_info.hh
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_tone_map_simple)
+ .local_group_size(16, 16)
+ .push_constant(Type::FLOAT, "luminance_scale")
+ .push_constant(Type::FLOAT, "luminance_scale_blend_factor")
+ .push_constant(Type::FLOAT, "inverse_gamma")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_tone_map_simple.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/gpu_shader_icon_frag.glsl b/source/blender/gpu/shaders/gpu_shader_icon_frag.glsl
new file mode 100644
index 00000000000..4452349f23c
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_icon_frag.glsl
@@ -0,0 +1,42 @@
+/**
+ * Draw the icons, leaving a semi-transparent rectangle on top of the icon.
+ *
+ * The top-left corner of the rectangle is rounded and drawned with anti-alias.
+ * The anti-alias is done by transitioning from the outer to the inner radius of
+ * the rounded corner, and the rectangle sides.
+ */
+
+void main()
+{
+ /* Top-left rounded corner parameters. */
+ const float circle_radius_outer = 0.1;
+ const float circle_radius_inner = 0.075;
+
+ /**
+ * Add a bit transparency to see a bit of the icon, without
+ * getting on the way of readability. */
+ const float mask_transparency = 0.25;
+
+ vec2 circle_center = vec2(circle_radius_outer - text_width, 0.5);
+ fragColor = texture(image, texCoord_interp) * color;
+
+ /* radius in icon space (1 is the icon width). */
+ float radius = length(mask_coord_interp - circle_center);
+ float mask = smoothstep(circle_radius_inner, circle_radius_outer, radius);
+
+ bool lower_half = mask_coord_interp.y < circle_center.y;
+ bool right_half = mask_coord_interp.x > circle_center.x;
+
+ if (right_half && mask_coord_interp.y < circle_center.y + circle_radius_outer) {
+ mask = smoothstep(circle_center.y + circle_radius_inner,
+ circle_center.y + circle_radius_outer,
+ mask_coord_interp.y);
+ }
+ if (lower_half && mask_coord_interp.x > circle_center.x - circle_radius_outer) {
+ mask = smoothstep(circle_center.x - circle_radius_inner,
+ circle_center.x - circle_radius_outer,
+ mask_coord_interp.x);
+ }
+
+ fragColor = mix(vec4(0.0), fragColor, max(mask_transparency, mask));
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_icon_vert.glsl b/source/blender/gpu/shaders/gpu_shader_icon_vert.glsl
new file mode 100644
index 00000000000..25f64bfe0b6
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_icon_vert.glsl
@@ -0,0 +1,37 @@
+/**
+ * Simple shader that just draw one icon at the specified location
+ * does not need any vertex input (producing less call to immBegin/End)
+ */
+
+void main()
+{
+ vec2 uv;
+ vec2 co;
+
+ if (gl_VertexID == 0) {
+ co = rect_geom.xw;
+ uv = rect_icon.xw;
+ mask_coord_interp = vec2(0, 1);
+ }
+ else if (gl_VertexID == 1) {
+ co = rect_geom.xy;
+ uv = rect_icon.xy;
+ mask_coord_interp = vec2(0, 0);
+ }
+ else if (gl_VertexID == 2) {
+ co = rect_geom.zw;
+ uv = rect_icon.zw;
+ mask_coord_interp = vec2(1, 1);
+ }
+ else {
+ co = rect_geom.zy;
+ uv = rect_icon.zy;
+ mask_coord_interp = vec2(1, 0);
+ }
+
+ /* Put origin in lower right corner. */
+ mask_coord_interp.x -= 1;
+
+ gl_Position = ModelViewProjectionMatrix * vec4(co, 0.0f, 1.0f);
+ texCoord_interp = uv;
+}
diff --git a/source/blender/gpu/shaders/infos/gpu_interface_info.hh b/source/blender/gpu/shaders/infos/gpu_interface_info.hh
index d77c65e48a7..060def16f81 100644
--- a/source/blender/gpu/shaders/infos/gpu_interface_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_interface_info.hh
@@ -18,3 +18,6 @@ GPU_SHADER_INTERFACE_INFO(smooth_radii_outline_iface, "").smooth(Type::VEC4, "ra
GPU_SHADER_INTERFACE_INFO(flat_color_smooth_tex_coord_interp_iface, "")
.flat(Type::VEC4, "finalColor")
.smooth(Type::VEC2, "texCoord_interp");
+GPU_SHADER_INTERFACE_INFO(smooth_icon_interp_iface, "")
+ .smooth(Type::VEC2, "texCoord_interp")
+ .smooth(Type::VEC2, "mask_coord_interp");
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_icon_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_icon_info.hh
new file mode 100644
index 00000000000..3d4077bdb09
--- /dev/null
+++ b/source/blender/gpu/shaders/infos/gpu_shader_icon_info.hh
@@ -0,0 +1,22 @@
+/* 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_icon)
+ .vertex_out(smooth_icon_interp_iface)
+ .fragment_out(0, Type::VEC4, "fragColor")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::VEC4, "color")
+ .push_constant(Type::VEC4, "rect_icon")
+ .push_constant(Type::VEC4, "rect_geom")
+ .push_constant(Type::FLOAT, "text_width")
+ .sampler(0, ImageType::FLOAT_2D, "image")
+ .vertex_source("gpu_shader_icon_vert.glsl")
+ .fragment_source("gpu_shader_icon_frag.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/vulkan/vk_backend.cc b/source/blender/gpu/vulkan/vk_backend.cc
new file mode 100644
index 00000000000..00bc43333d6
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_backend.cc
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "vk_backend.hh"
+
+#include "vk_batch.hh"
+#include "vk_context.hh"
+#include "vk_drawlist.hh"
+#include "vk_framebuffer.hh"
+#include "vk_index_buffer.hh"
+#include "vk_query.hh"
+#include "vk_shader.hh"
+#include "vk_storage_buffer.hh"
+#include "vk_texture.hh"
+#include "vk_uniform_buffer.hh"
+#include "vk_vertex_buffer.hh"
+
+namespace blender::gpu {
+
+void VKBackend::delete_resources()
+{
+}
+
+void VKBackend::samplers_update()
+{
+}
+
+void VKBackend::compute_dispatch(int /*groups_x_len*/, int /*groups_y_len*/, int /*groups_z_len*/)
+{
+}
+
+void VKBackend::compute_dispatch_indirect(StorageBuf * /*indirect_buf*/)
+{
+}
+
+Context *VKBackend::context_alloc(void * /*ghost_window*/, void * /*ghost_context*/)
+{
+ return new VKContext();
+}
+
+Batch *VKBackend::batch_alloc()
+{
+ return new VKBatch();
+}
+
+DrawList *VKBackend::drawlist_alloc(int /*list_length*/)
+{
+ return new VKDrawList();
+}
+
+FrameBuffer *VKBackend::framebuffer_alloc(const char *name)
+{
+ return new VKFrameBuffer(name);
+}
+
+IndexBuf *VKBackend::indexbuf_alloc()
+{
+ return new VKIndexBuffer();
+}
+
+QueryPool *VKBackend::querypool_alloc()
+{
+ return new VKQueryPool();
+}
+
+Shader *VKBackend::shader_alloc(const char *name)
+{
+ return new VKShader(name);
+}
+
+Texture *VKBackend::texture_alloc(const char *name)
+{
+ return new VKTexture(name);
+}
+
+UniformBuf *VKBackend::uniformbuf_alloc(int size, const char *name)
+{
+ return new VKUniformBuffer(size, name);
+}
+
+StorageBuf *VKBackend::storagebuf_alloc(int size, GPUUsageType /*usage*/, const char *name)
+{
+ return new VKStorageBuffer(size, name);
+}
+
+VertBuf *VKBackend::vertbuf_alloc()
+{
+ return new VKVertexBuffer();
+}
+
+void VKBackend::render_begin()
+{
+}
+
+void VKBackend::render_end()
+{
+}
+
+void VKBackend::render_step()
+{
+}
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_backend.hh b/source/blender/gpu/vulkan/vk_backend.hh
new file mode 100644
index 00000000000..549478586e8
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_backend.hh
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "gpu_backend.hh"
+
+namespace blender::gpu {
+
+class VKBackend : public GPUBackend {
+ public:
+ void delete_resources() override;
+
+ void samplers_update() override;
+ void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) override;
+ void compute_dispatch_indirect(StorageBuf *indirect_buf) override;
+
+ Context *context_alloc(void *ghost_window, void *ghost_context) override;
+
+ Batch *batch_alloc() override;
+ DrawList *drawlist_alloc(int list_length) override;
+ FrameBuffer *framebuffer_alloc(const char *name) override;
+ IndexBuf *indexbuf_alloc() override;
+ QueryPool *querypool_alloc() override;
+ Shader *shader_alloc(const char *name) override;
+ Texture *texture_alloc(const char *name) override;
+ UniformBuf *uniformbuf_alloc(int size, const char *name) override;
+ StorageBuf *storagebuf_alloc(int size, GPUUsageType usage, const char *name) override;
+ VertBuf *vertbuf_alloc() override;
+
+ /* Render Frame Coordination --
+ * Used for performing per-frame actions globally */
+ void render_begin() override;
+ void render_end() override;
+ void render_step() override;
+};
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_batch.cc b/source/blender/gpu/vulkan/vk_batch.cc
new file mode 100644
index 00000000000..a25f98a2e24
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_batch.cc
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "vk_batch.hh"
+
+namespace blender::gpu {
+
+void VKBatch::draw(int /*v_first*/, int /*v_count*/, int /*i_first*/, int /*i_count*/)
+{
+}
+
+void VKBatch::draw_indirect(GPUStorageBuf * /*indirect_buf*/, intptr_t /*offset*/)
+{
+}
+
+void VKBatch::multi_draw_indirect(GPUStorageBuf * /*indirect_buf*/,
+ int /*count*/,
+ intptr_t /*offset*/,
+ intptr_t /*stride*/)
+{
+}
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_batch.hh b/source/blender/gpu/vulkan/vk_batch.hh
new file mode 100644
index 00000000000..0f6df41606d
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_batch.hh
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "gpu_batch_private.hh"
+
+namespace blender::gpu {
+
+class VKBatch : public Batch {
+ public:
+ void draw(int v_first, int v_count, int i_first, int i_count) override;
+ void draw_indirect(GPUStorageBuf *indirect_buf, intptr_t offset) override;
+ void multi_draw_indirect(GPUStorageBuf *indirect_buf,
+ int count,
+ intptr_t offset,
+ intptr_t stride) override;
+};
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_context.cc b/source/blender/gpu/vulkan/vk_context.cc
new file mode 100644
index 00000000000..55b29ea4e2f
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_context.cc
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "vk_context.hh"
+
+namespace blender::gpu {
+
+void VKContext::activate()
+{
+}
+
+void VKContext::deactivate()
+{
+}
+
+void VKContext::begin_frame()
+{
+}
+
+void VKContext::end_frame()
+{
+}
+
+void VKContext::flush()
+{
+}
+
+void VKContext::finish()
+{
+}
+
+void VKContext::memory_statistics_get(int * /*total_mem*/, int * /*free_mem*/)
+{
+}
+
+void VKContext::debug_group_begin(const char *, int)
+{
+}
+
+void VKContext::debug_group_end()
+{
+}
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_context.hh b/source/blender/gpu/vulkan/vk_context.hh
new file mode 100644
index 00000000000..17292b891b6
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_context.hh
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "gpu_context_private.hh"
+
+namespace blender::gpu {
+
+class VKContext : public Context {
+ public:
+ VKContext()
+ {
+ }
+
+ void activate() override;
+ void deactivate() override;
+ void begin_frame() override;
+ void end_frame() override;
+
+ void flush() override;
+ void finish() override;
+
+ void memory_statistics_get(int *total_mem, int *free_mem) override;
+
+ void debug_group_begin(const char *, int) override;
+ void debug_group_end() override;
+};
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_drawlist.cc b/source/blender/gpu/vulkan/vk_drawlist.cc
new file mode 100644
index 00000000000..c8f3c736bb8
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_drawlist.cc
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "vk_drawlist.hh"
+
+namespace blender::gpu {
+
+void VKDrawList::append(GPUBatch * /*batch*/, int /*i_first*/, int /*i_count*/)
+{
+}
+
+void VKDrawList::submit()
+{
+}
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_drawlist.hh b/source/blender/gpu/vulkan/vk_drawlist.hh
new file mode 100644
index 00000000000..4707bf4bb26
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_drawlist.hh
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "gpu_drawlist_private.hh"
+
+namespace blender::gpu {
+
+class VKDrawList : public DrawList {
+ public:
+ void append(GPUBatch *batch, int i_first, int i_count) override;
+ void submit() override;
+};
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_framebuffer.cc b/source/blender/gpu/vulkan/vk_framebuffer.cc
new file mode 100644
index 00000000000..48b0685bf38
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_framebuffer.cc
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "vk_framebuffer.hh"
+
+namespace blender::gpu {
+
+void VKFrameBuffer::bind(bool /*enabled_srgb*/)
+{
+}
+
+bool VKFrameBuffer::check(char /*err_out*/[256])
+{
+ return false;
+}
+
+void VKFrameBuffer::clear(eGPUFrameBufferBits /*buffers*/,
+ const float /*clear_col*/[4],
+ float /*clear_depth*/,
+ uint /*clear_stencil*/)
+{
+}
+
+void VKFrameBuffer::clear_multi(const float (*/*clear_col*/)[4])
+{
+}
+
+void VKFrameBuffer::clear_attachment(GPUAttachmentType /*type*/,
+ eGPUDataFormat /*data_format*/,
+ const void * /*clear_value*/)
+{
+}
+
+void VKFrameBuffer::attachment_set_loadstore_op(GPUAttachmentType /*type*/,
+ eGPULoadOp /*load_action*/,
+ eGPUStoreOp /*store_action*/)
+{
+}
+
+void VKFrameBuffer::read(eGPUFrameBufferBits /*planes*/,
+ eGPUDataFormat /*format*/,
+ const int /*area*/[4],
+ int /*channel_len*/,
+ int /*slot*/,
+ void * /*r_data*/)
+{
+}
+
+void VKFrameBuffer::blit_to(eGPUFrameBufferBits /*planes*/,
+ int /*src_slot*/,
+ FrameBuffer * /*dst*/,
+ int /*dst_slot*/,
+ int /*dst_offset_x*/,
+ int /*dst_offset_y*/)
+{
+}
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_framebuffer.hh b/source/blender/gpu/vulkan/vk_framebuffer.hh
new file mode 100644
index 00000000000..632d45ce709
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_framebuffer.hh
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "gpu_framebuffer_private.hh"
+
+namespace blender::gpu {
+
+class VKFrameBuffer : public FrameBuffer {
+ public:
+ VKFrameBuffer(const char *name) : FrameBuffer(name)
+ {
+ }
+
+ void bind(bool enabled_srgb) override;
+ bool check(char err_out[256]) override;
+ void clear(eGPUFrameBufferBits buffers,
+ const float clear_col[4],
+ float clear_depth,
+ uint clear_stencil) override;
+ void clear_multi(const float (*clear_col)[4]) override;
+ void clear_attachment(GPUAttachmentType type,
+ eGPUDataFormat data_format,
+ const void *clear_value) override;
+
+ void attachment_set_loadstore_op(GPUAttachmentType type,
+ eGPULoadOp load_action,
+ eGPUStoreOp store_action) override;
+
+ void read(eGPUFrameBufferBits planes,
+ eGPUDataFormat format,
+ const int area[4],
+ int channel_len,
+ int slot,
+ void *r_data) override;
+
+ void blit_to(eGPUFrameBufferBits planes,
+ int src_slot,
+ FrameBuffer *dst,
+ int dst_slot,
+ int dst_offset_x,
+ int dst_offset_y) override;
+};
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_index_buffer.cc b/source/blender/gpu/vulkan/vk_index_buffer.cc
new file mode 100644
index 00000000000..119a617a159
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_index_buffer.cc
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "vk_index_buffer.hh"
+
+namespace blender::gpu {
+
+void VKIndexBuffer::upload_data()
+{
+}
+
+void VKIndexBuffer::bind_as_ssbo(uint /*binding*/)
+{
+}
+
+const uint32_t *VKIndexBuffer::read() const
+{
+ return 0;
+}
+
+void VKIndexBuffer::update_sub(uint /*start*/, uint /*len*/, const void * /*data*/)
+{
+}
+
+void VKIndexBuffer::strip_restart_indices()
+{
+}
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_index_buffer.hh b/source/blender/gpu/vulkan/vk_index_buffer.hh
new file mode 100644
index 00000000000..f002d5581c7
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_index_buffer.hh
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "gpu_index_buffer_private.hh"
+
+namespace blender::gpu {
+
+class VKIndexBuffer : public IndexBuf {
+ public:
+ void upload_data() override;
+
+ void bind_as_ssbo(uint binding) override;
+
+ const uint32_t *read() const override;
+
+ void update_sub(uint start, uint len, const void *data) override;
+
+ private:
+ void strip_restart_indices() override;
+};
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_query.cc b/source/blender/gpu/vulkan/vk_query.cc
new file mode 100644
index 00000000000..e24fe54e5d0
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_query.cc
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "vk_query.hh"
+
+namespace blender::gpu {
+
+void VKQueryPool::init(GPUQueryType /*type*/)
+{
+}
+
+void VKQueryPool::begin_query()
+{
+}
+
+void VKQueryPool::end_query()
+{
+}
+
+void VKQueryPool::get_occlusion_result(MutableSpan<uint32_t> /*r_values*/)
+{
+}
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_query.hh b/source/blender/gpu/vulkan/vk_query.hh
new file mode 100644
index 00000000000..36558ef9549
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_query.hh
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "gpu_query.hh"
+
+namespace blender::gpu {
+
+class VKQueryPool : public QueryPool {
+ public:
+ void init(GPUQueryType type) override;
+ void begin_query() override;
+ void end_query() override;
+ void get_occlusion_result(MutableSpan<uint32_t> r_values) override;
+};
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_shader.cc b/source/blender/gpu/vulkan/vk_shader.cc
new file mode 100644
index 00000000000..d628f3eb851
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_shader.cc
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "vk_shader.hh"
+
+namespace blender::gpu {
+void VKShader::vertex_shader_from_glsl(MutableSpan<const char *> /*sources*/)
+{
+}
+
+void VKShader::geometry_shader_from_glsl(MutableSpan<const char *> /*sources*/)
+{
+}
+
+void VKShader::fragment_shader_from_glsl(MutableSpan<const char *> /*sources*/)
+{
+}
+
+void VKShader::compute_shader_from_glsl(MutableSpan<const char *> /*sources*/)
+{
+}
+
+bool VKShader::finalize(const shader::ShaderCreateInfo * /*info*/)
+{
+ return false;
+}
+
+void VKShader::transform_feedback_names_set(Span<const char *> /*name_list*/,
+ eGPUShaderTFBType /*geom_type*/)
+{
+}
+
+bool VKShader::transform_feedback_enable(GPUVertBuf *)
+{
+ return false;
+}
+
+void VKShader::transform_feedback_disable()
+{
+}
+
+void VKShader::bind()
+{
+}
+
+void VKShader::unbind()
+{
+}
+
+void VKShader::uniform_float(int /*location*/,
+ int /*comp_len*/,
+ int /*array_size*/,
+ const float * /*data*/)
+{
+}
+void VKShader::uniform_int(int /*location*/,
+ int /*comp_len*/,
+ int /*array_size*/,
+ const int * /*data*/)
+{
+}
+
+std::string VKShader::resources_declare(const shader::ShaderCreateInfo & /*info*/) const
+{
+ return std::string();
+}
+
+std::string VKShader::vertex_interface_declare(const shader::ShaderCreateInfo & /*info*/) const
+{
+ return std::string();
+}
+
+std::string VKShader::fragment_interface_declare(const shader::ShaderCreateInfo & /*info*/) const
+{
+ return std::string();
+}
+
+std::string VKShader::geometry_interface_declare(const shader::ShaderCreateInfo & /*info*/) const
+{
+ return std::string();
+}
+
+std::string VKShader::geometry_layout_declare(const shader::ShaderCreateInfo & /*info*/) const
+{
+ return std::string();
+}
+
+std::string VKShader::compute_layout_declare(const shader::ShaderCreateInfo & /*info*/) const
+{
+ return std::string();
+}
+
+int VKShader::program_handle_get() const
+{
+ return -1;
+}
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_shader.hh b/source/blender/gpu/vulkan/vk_shader.hh
new file mode 100644
index 00000000000..9ab0aca67eb
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_shader.hh
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "gpu_shader_private.hh"
+
+namespace blender::gpu {
+
+class VKShader : public Shader {
+ public:
+ VKShader(const char *name) : Shader(name)
+ {
+ }
+
+ void vertex_shader_from_glsl(MutableSpan<const char *> sources) override;
+ void geometry_shader_from_glsl(MutableSpan<const char *> sources) override;
+ void fragment_shader_from_glsl(MutableSpan<const char *> sources) override;
+ void compute_shader_from_glsl(MutableSpan<const char *> sources) override;
+ bool finalize(const shader::ShaderCreateInfo *info = nullptr) override;
+
+ void transform_feedback_names_set(Span<const char *> name_list,
+ eGPUShaderTFBType geom_type) override;
+ bool transform_feedback_enable(GPUVertBuf *) override;
+ void transform_feedback_disable() override;
+
+ void bind() override;
+ void unbind() override;
+
+ void uniform_float(int location, int comp_len, int array_size, const float *data) override;
+ void uniform_int(int location, int comp_len, int array_size, const int *data) override;
+
+ std::string resources_declare(const shader::ShaderCreateInfo &info) const override;
+ std::string vertex_interface_declare(const shader::ShaderCreateInfo &info) const override;
+ std::string fragment_interface_declare(const shader::ShaderCreateInfo &info) const override;
+ std::string geometry_interface_declare(const shader::ShaderCreateInfo &info) const override;
+ std::string geometry_layout_declare(const shader::ShaderCreateInfo &info) const override;
+ std::string compute_layout_declare(const shader::ShaderCreateInfo &info) const override;
+
+ /* DEPRECATED: Kept only because of BGL API. */
+ int program_handle_get() const override;
+};
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_storage_buffer.cc b/source/blender/gpu/vulkan/vk_storage_buffer.cc
new file mode 100644
index 00000000000..2b6fda0547d
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_storage_buffer.cc
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "vk_vertex_buffer.hh"
+
+#include "vk_storage_buffer.hh"
+
+namespace blender::gpu {
+
+void VKStorageBuffer::update(const void * /*data*/)
+{
+}
+
+void VKStorageBuffer::bind(int /*slot*/)
+{
+}
+
+void VKStorageBuffer::unbind()
+{
+}
+
+void VKStorageBuffer::clear(eGPUTextureFormat /* internal_format*/,
+ eGPUDataFormat /*data_format*/,
+ void * /*data*/)
+{
+}
+void VKStorageBuffer::copy_sub(VertBuf * /*src*/,
+ uint /*dst_offset*/,
+ uint /*src_offset*/,
+ uint /*copy_size*/)
+{
+}
+
+void VKStorageBuffer::read(void * /*data*/)
+{
+}
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_storage_buffer.hh b/source/blender/gpu/vulkan/vk_storage_buffer.hh
new file mode 100644
index 00000000000..a51f89f627a
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_storage_buffer.hh
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "GPU_texture.h"
+
+#include "gpu_storage_buffer_private.hh"
+
+namespace blender::gpu {
+
+class VKStorageBuffer : public StorageBuf {
+ public:
+ VKStorageBuffer(int size, const char *name) : StorageBuf(size, name)
+ {
+ }
+
+ void update(const void *data) override;
+ 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;
+ void read(void *data) override;
+};
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_texture.cc b/source/blender/gpu/vulkan/vk_texture.cc
new file mode 100644
index 00000000000..ea5379e6572
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_texture.cc
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "vk_texture.hh"
+
+namespace blender::gpu {
+
+void VKTexture::generate_mipmap()
+{
+}
+
+void VKTexture::copy_to(Texture * /*tex*/)
+{
+}
+
+void VKTexture::clear(eGPUDataFormat /*format*/, const void * /*data*/)
+{
+}
+
+void VKTexture::swizzle_set(const char /*swizzle_mask*/[4])
+{
+}
+
+void VKTexture::stencil_texture_mode_set(bool /*use_stencil*/)
+{
+}
+
+void VKTexture::mip_range_set(int /*min*/, int /*max*/)
+{
+}
+
+void *VKTexture::read(int /*mip*/, eGPUDataFormat /*format*/)
+{
+ return nullptr;
+}
+
+void VKTexture::update_sub(int /*mip*/,
+ int /*offset*/[3],
+ int /*extent*/[3],
+ eGPUDataFormat /*format*/,
+ const void * /*data*/)
+{
+}
+
+/* TODO(fclem): Legacy. Should be removed at some point. */
+uint VKTexture::gl_bindcode_get() const
+{
+ return 0;
+}
+
+bool VKTexture::init_internal()
+{
+ return false;
+}
+
+bool VKTexture::init_internal(GPUVertBuf * /*vbo*/)
+{
+ return false;
+}
+
+bool VKTexture::init_internal(const GPUTexture * /*src*/, int /*mip_offset*/, int /*layer_offset*/)
+{
+ return false;
+}
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_texture.hh b/source/blender/gpu/vulkan/vk_texture.hh
new file mode 100644
index 00000000000..93094b7e540
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_texture.hh
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "gpu_texture_private.hh"
+
+namespace blender::gpu {
+
+class VKTexture : public Texture {
+ public:
+ VKTexture(const char *name) : Texture(name)
+ {
+ }
+
+ void generate_mipmap() override;
+ void copy_to(Texture *tex) override;
+ void clear(eGPUDataFormat format, const void *data) override;
+ void swizzle_set(const char swizzle_mask[4]) override;
+ void stencil_texture_mode_set(bool use_stencil) override;
+ void mip_range_set(int min, int max) override;
+ void *read(int mip, eGPUDataFormat format) override;
+ void update_sub(
+ int mip, int offset[3], int extent[3], eGPUDataFormat format, const void *data) override;
+
+ /* TODO(fclem): Legacy. Should be removed at some point. */
+ uint gl_bindcode_get() const override;
+
+ protected:
+ bool init_internal() override;
+ bool init_internal(GPUVertBuf *vbo) override;
+ bool init_internal(const GPUTexture *src, int mip_offset, int layer_offset) override;
+};
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_uniform_buffer.cc b/source/blender/gpu/vulkan/vk_uniform_buffer.cc
new file mode 100644
index 00000000000..8ef5b19273a
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_uniform_buffer.cc
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "vk_uniform_buffer.hh"
+
+namespace blender::gpu {
+
+void VKUniformBuffer::update(const void * /*data*/)
+{
+}
+
+void VKUniformBuffer::bind(int /*slot*/)
+{
+}
+
+void VKUniformBuffer::unbind()
+{
+}
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_uniform_buffer.hh b/source/blender/gpu/vulkan/vk_uniform_buffer.hh
new file mode 100644
index 00000000000..f086a7aa391
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_uniform_buffer.hh
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "gpu_uniform_buffer_private.hh"
+
+namespace blender::gpu {
+
+class VKUniformBuffer : public UniformBuf {
+ public:
+ VKUniformBuffer(int size, const char *name) : UniformBuf(size, name)
+ {
+ }
+
+ void update(const void *data) override;
+ void bind(int slot) override;
+ void unbind() override;
+};
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_vertex_buffer.cc b/source/blender/gpu/vulkan/vk_vertex_buffer.cc
new file mode 100644
index 00000000000..5791e20fb30
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_vertex_buffer.cc
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "vk_vertex_buffer.hh"
+
+namespace blender::gpu {
+
+void VKVertexBuffer::bind_as_ssbo(uint /*binding*/)
+{
+}
+
+void VKVertexBuffer::bind_as_texture(uint /*binding*/)
+{
+}
+
+void VKVertexBuffer::wrap_handle(uint64_t /*handle*/)
+{
+}
+
+void VKVertexBuffer::update_sub(uint /*start*/, uint /*len*/, const void * /*data*/)
+{
+}
+
+const void *VKVertexBuffer::read() const
+{
+ return nullptr;
+}
+
+void *VKVertexBuffer::unmap(const void * /*mapped_data*/) const
+{
+ return nullptr;
+}
+
+void VKVertexBuffer::acquire_data()
+{
+}
+
+void VKVertexBuffer::resize_data()
+{
+}
+
+void VKVertexBuffer::release_data()
+{
+}
+
+void VKVertexBuffer::upload_data()
+{
+}
+
+void VKVertexBuffer::duplicate_data(VertBuf * /*dst*/)
+{
+}
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/vulkan/vk_vertex_buffer.hh b/source/blender/gpu/vulkan/vk_vertex_buffer.hh
new file mode 100644
index 00000000000..84ccc65bcdf
--- /dev/null
+++ b/source/blender/gpu/vulkan/vk_vertex_buffer.hh
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "gpu_vertex_buffer_private.hh"
+
+namespace blender::gpu {
+
+class VKVertexBuffer : public VertBuf {
+ public:
+ void bind_as_ssbo(uint binding) override;
+ void bind_as_texture(uint binding) override;
+ void wrap_handle(uint64_t handle) override;
+
+ void update_sub(uint start, uint len, const void *data) override;
+ const void *read() const override;
+ void *unmap(const void *mapped_data) const override;
+
+ protected:
+ void acquire_data() override;
+ void resize_data() override;
+ void release_data() override;
+ void upload_data() override;
+ void duplicate_data(VertBuf *dst) override;
+};
+
+} // namespace blender::gpu \ No newline at end of file