diff options
Diffstat (limited to 'source/blender/gpu')
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 |