Changeset View
Standalone View
source/blender/gpu/metal/mtl_shader.hh
- This file was added.
| /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||||||||||
| /** \file | ||||||||||
| * \ingroup gpu | ||||||||||
| */ | ||||||||||
| #pragma once | ||||||||||
| #include "MEM_guardedalloc.h" | ||||||||||
| #include "GPU_batch.h" | ||||||||||
| #include "GPU_capabilities.h" | ||||||||||
| #include "GPU_shader.h" | ||||||||||
| #include "GPU_vertex_format.h" | ||||||||||
| #include <Metal/Metal.h> | ||||||||||
| #include <QuartzCore/QuartzCore.h> | ||||||||||
| #include <functional> | ||||||||||
| #include <unordered_map> | ||||||||||
| #include <mutex> | ||||||||||
| #include <thread> | ||||||||||
| #include "mtl_framebuffer.hh" | ||||||||||
| #include "mtl_shader_interface.hh" | ||||||||||
| #include "mtl_shader_shared.h" | ||||||||||
| #include "mtl_state.hh" | ||||||||||
| #include "mtl_texture.hh" | ||||||||||
| #include "gpu_shader_create_info.hh" | ||||||||||
| #include "gpu_shader_private.hh" | ||||||||||
| namespace blender::gpu { | ||||||||||
| class MTLShaderInterface; | ||||||||||
| class MTLContext; | ||||||||||
| /* Debug control. */ | ||||||||||
| #define MTL_SHADER_DEBUG_EXPORT_SOURCE 1 | ||||||||||
| #define MTL_SHADER_TRANSLATION_DEBUG_OUTPUT 0 | ||||||||||
| /* Separate print used only during development and debugging. */ | ||||||||||
| #if MTL_SHADER_TRANSLATION_DEBUG_OUTPUT | ||||||||||
| # define shader_debug_printf printf | ||||||||||
| #else | ||||||||||
| # define shader_debug_printf(...) /* Null print. */ | ||||||||||
| #endif | ||||||||||
| /* Desired reflection data for a buffer binding. */ | ||||||||||
| struct MTLBufferArgumentData { | ||||||||||
| uint32_t index; | ||||||||||
| uint32_t size; | ||||||||||
| uint32_t alignment; | ||||||||||
| bool active; | ||||||||||
| }; | ||||||||||
| /* Metal Render Pipeline State Instance. */ | ||||||||||
| struct MTLRenderPipelineStateInstance { | ||||||||||
| /* Function instances with specialisation. | ||||||||||
| * Required for argument encoder construction. */ | ||||||||||
| id<MTLFunction> vert; | ||||||||||
| id<MTLFunction> frag; | ||||||||||
| /* PSO handle. */ | ||||||||||
| id<MTLRenderPipelineState> pso; | ||||||||||
| /** Derived information. */ | ||||||||||
| /* Unique index for PSO variant. */ | ||||||||||
| uint32_t shader_pso_index; | ||||||||||
| /* Base bind index for binding uniform buffers, offset based on other | ||||||||||
| * bound buffers such as vertex buffers, as the count can vary. */ | ||||||||||
| int base_uniform_buffer_index; | ||||||||||
| /* buffer bind slot used for null attributes (-1 if not needed). */ | ||||||||||
| int null_attribute_buffer_index; | ||||||||||
| /* buffer bind used for transform feedback output buffer. */ | ||||||||||
| int transform_feedback_buffer_index; | ||||||||||
| /** Reflection Data. | ||||||||||
fclem: Could we use a shared header for these? similar to `draw_shader_shared.h`. | ||||||||||
| * Currently used to verify whether uniform buffers of incorrect sizes being bound, due to left | ||||||||||
| * over bindings being used for slots that did not need updating for a particular draw. Metal | ||||||||||
| * Backend over-generates bindings due to detecting their presence, though in many cases, the | ||||||||||
| * bindings in the source are not all used for a given shader. | ||||||||||
| * This information can also be used to eliminate redundant/unused bindings. */ | ||||||||||
| bool reflection_data_available; | ||||||||||
| blender::Vector<MTLBufferArgumentData> buffer_bindings_reflection_data_vert; | ||||||||||
| blender::Vector<MTLBufferArgumentData> buffer_bindings_reflection_data_frag; | ||||||||||
| }; | ||||||||||
| /* MTLShaderBuilder source wrapper used during initial compilation. */ | ||||||||||
| struct MTLShaderBuilder { | ||||||||||
| NSString *msl_source_vert_ = @""; | ||||||||||
| NSString *msl_source_frag_ = @""; | ||||||||||
| /* Generated GLSL source used during compilation. */ | ||||||||||
| std::string glsl_vertex_source_ = ""; | ||||||||||
| std::string glsl_fragment_source_ = ""; | ||||||||||
| /* Indicates whether source code has been provided via MSL directly. */ | ||||||||||
| bool source_from_msl_ = false; | ||||||||||
| }; | ||||||||||
| /** | ||||||||||
| * MTLShader implements shader compilation, Pipeline State Object (PSO) | ||||||||||
| * creation for rendering and uniform data binding. | ||||||||||
| * Shaders can either be created from native MSL, or generated | ||||||||||
| * from a GLSL source shader using GPUShaderCreateInfo. | ||||||||||
| * | ||||||||||
| * Shader creation process: | ||||||||||
| * - Create MTLShader: | ||||||||||
| * - Convert GLSL to MSL source if required. | ||||||||||
| * - set MSL source. | ||||||||||
| * - set Vertex/Fragment function names. | ||||||||||
| * - Create and populate MTLShaderInterface. | ||||||||||
| **/ | ||||||||||
| class MTLShader : public Shader { | ||||||||||
| friend shader::ShaderCreateInfo; | ||||||||||
| friend shader::StageInterfaceInfo; | ||||||||||
| public: | ||||||||||
| /* Cached SSBO vertex fetch attribute uniform locations. */ | ||||||||||
| int uni_ssbo_input_prim_type_loc = -1; | ||||||||||
| int uni_ssbo_input_vert_count_loc = -1; | ||||||||||
| int uni_ssbo_uses_indexed_rendering = -1; | ||||||||||
| int uni_ssbo_uses_index_mode_u16 = -1; | ||||||||||
| private: | ||||||||||
| /* Context Handle. */ | ||||||||||
| MTLContext *context_ = nullptr; | ||||||||||
| /** Transform Feedback. */ | ||||||||||
| /* Transform feedback mode. */ | ||||||||||
| eGPUShaderTFBType transform_feedback_type_ = GPU_SHADER_TFB_NONE; | ||||||||||
| /* Transform feedback outputs written to TFB buffer. */ | ||||||||||
| blender::Vector<std::string> tf_output_name_list_; | ||||||||||
| /* Whether transform feedback is currently active. */ | ||||||||||
| bool transform_feedback_active_ = false; | ||||||||||
| /* Vertex buffer to write transform feedback data into. */ | ||||||||||
| GPUVertBuf *transform_feedback_vertbuf_ = nullptr; | ||||||||||
| /** Shader source code. */ | ||||||||||
| MTLShaderBuilder *shd_builder_ = nullptr; | ||||||||||
| NSString *vertex_function_name_ = @""; | ||||||||||
| NSString *fragment_function_name_ = @""; | ||||||||||
| /** Compiled shader resources. */ | ||||||||||
| id<MTLLibrary> shader_library_vert_ = nil; | ||||||||||
| id<MTLLibrary> shader_library_frag_ = nil; | ||||||||||
| bool valid_ = false; | ||||||||||
| /** Render pipeline state and PSO caching. */ | ||||||||||
| /* Metal API Descriptor used for creation of unique PSOs based on rendering state. */ | ||||||||||
| MTLRenderPipelineDescriptor *pso_descriptor_ = nil; | ||||||||||
| /* Metal backend struct containing all high-level pipeline state parameters | ||||||||||
| * which contribute to instantiation of a unique PSO. */ | ||||||||||
| MTLRenderPipelineStateDescriptor current_pipeline_state_; | ||||||||||
| /* Cache of compiled PipelineStateObjects. */ | ||||||||||
| blender::Map<MTLRenderPipelineStateDescriptor, MTLRenderPipelineStateInstance *> pso_cache_; | ||||||||||
Not Done Inline Actions
fclem: | ||||||||||
| /* True to enable multi-layered rendering support. */ | ||||||||||
| bool uses_mtl_array_index_ = false; | ||||||||||
| /** SSBO Vertex fetch pragma options. */ | ||||||||||
| /* Indicates whether to pass in VertexBuffer's as regular buffer bindings | ||||||||||
| * and perform vertex assembly manually, rather than using Stage-in. | ||||||||||
| * This is used to give a vertex shader full access to all of the | ||||||||||
| * vertex data. | ||||||||||
| * This is primarily used for optimisation techniques and | ||||||||||
| * alternative solutions for Geometry-shaders which are unsupported | ||||||||||
| * by Metal. */ | ||||||||||
| bool use_ssbo_vertex_fetch_mode_ = false; | ||||||||||
| /* Output primitive type when rendering sing ssbo_vertex_fetch. */ | ||||||||||
| MTLPrimitiveType ssbo_vertex_fetch_output_prim_type_; | ||||||||||
| /* Output vertices per original vertex shader instance. | ||||||||||
| * This number will be multiplied by the number of input primitives | ||||||||||
| * from the source draw call. */ | ||||||||||
| uint32_t ssbo_vertex_fetch_output_num_verts_ = 0; | ||||||||||
| bool ssbo_vertex_attribute_bind_active_ = false; | ||||||||||
| int ssbo_vertex_attribute_bind_mask_ = 0; | ||||||||||
| bool ssbo_vbo_slot_used_[MTL_SSBO_VERTEX_FETCH_MAX_VBOS]; | ||||||||||
| struct ShaderSSBOAttributeBinding { | ||||||||||
| int attribute_index = -1; | ||||||||||
| int uniform_stride; | ||||||||||
| int uniform_offset; | ||||||||||
| int uniform_fetchmode; | ||||||||||
| int uniform_vbo_id; | ||||||||||
| int uniform_attr_type; | ||||||||||
| }; | ||||||||||
| ShaderSSBOAttributeBinding cached_ssbo_attribute_bindings_[MTL_MAX_VERTEX_INPUT_ATTRIBUTES] = {}; | ||||||||||
| /* Metal Shader Uniform data store. | ||||||||||
| * This blocks is used to store current shader push_constant | ||||||||||
| * data before it is submitted to the GPU. This is currently | ||||||||||
| * stored per shader instance, though depending on GPU module | ||||||||||
| * functionality, this could potentially be a global data store. | ||||||||||
| * This data is associated with the PushConstantBlock, which is | ||||||||||
| * always at index zero in the UBO list. */ | ||||||||||
| void *push_constant_data_ = nullptr; | ||||||||||
| bool push_constant_modified_ = false; | ||||||||||
| public: | ||||||||||
| MTLShader(MTLContext *ctx, const char *name); | ||||||||||
| MTLShader(MTLContext *ctx, | ||||||||||
| MTLShaderInterface *interface, | ||||||||||
| const char *name, | ||||||||||
| NSString *input_vertex_source, | ||||||||||
| NSString *input_fragment_source, | ||||||||||
| NSString *vertex_function_name_, | ||||||||||
Not Done Inline ActionsThis is weird to have it here. Would have thought this would go more into a metal_state.hh conceptually. It has no direct link to shaders excepth the MTLVertexDescriptor. fclem: This is weird to have it here. Would have thought this would go more into a `metal_state.hh`… | ||||||||||
Not Done Inline ActionsYeah, this struct could likely be moved to a more global location. The thinking of having it local to the shader class was that this contains a subset of global state which feeds PSO creation (which is shader specific), and is then used as a key in the hash map of previously compiled PSOs. In this case, PSO backend compilation combines the intermediate representation of the compiled shader library with the render pipeline state to compile the final shader program which will execute on the GPU. Currently, this struct has data copied in prior to PSO creation to ensure everything is up to date based on the current pipeline state. This mechanism could still happen in the same way, to avoid double-population of state. There is also a separation between this struct and the MTLGlobalPipelineState/MTLRenderPassState, as this one contains only the hashable subset which feeds the MTLRenderPipelineDescriptor for shader-specific PSO creation. The reason this is also a member struct rather than a local one is such that MTLBatch and MTLImmediate can directly populate the Vertex descriptor in this struct. However, this could most likely be replaced with a global vertex descriptor in a global copy of this struct. MichaelPW: Yeah, this struct could likely be moved to a more global location. The thinking of having it… | ||||||||||
| NSString *fragment_function_name_); | ||||||||||
| ~MTLShader(); | ||||||||||
| /* Assign GLSL source. */ | ||||||||||
| void vertex_shader_from_glsl(MutableSpan<const char *> sources) override; | ||||||||||
| void geometry_shader_from_glsl(MutableSpan<const char *> sources) override; | ||||||||||
Not Done Inline Actions
fclem: | ||||||||||
| void fragment_shader_from_glsl(MutableSpan<const char *> sources) override; | ||||||||||
| void compute_shader_from_glsl(MutableSpan<const char *> sources) override; | ||||||||||
| /* Compile and build - Return true if successful. */ | ||||||||||
| bool finalize(const shader::ShaderCreateInfo *info = nullptr) override; | ||||||||||
| /* Utility. */ | ||||||||||
| bool is_valid() | ||||||||||
| { | ||||||||||
| return valid_; | ||||||||||
| } | ||||||||||
| MTLRenderPipelineStateDescriptor &get_current_pipeline_state() | ||||||||||
| { | ||||||||||
| return current_pipeline_state_; | ||||||||||
| } | ||||||||||
| MTLShaderInterface *get_interface() | ||||||||||
| { | ||||||||||
| return static_cast<MTLShaderInterface *>(this->interface); | ||||||||||
| } | ||||||||||
| void *get_push_constant_data() | ||||||||||
| { | ||||||||||
| return push_constant_data_; | ||||||||||
| } | ||||||||||
| /* Shader source generators from create-info. | ||||||||||
| * These aren't all used by Metal, as certain parts of source code generation | ||||||||||
| * for shader entry-points and resource mapping occur during `finalize`. */ | ||||||||||
| 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; | ||||||||||
| void transform_feedback_names_set(Span<const char *> name_list, | ||||||||||
| const eGPUShaderTFBType geom_type) override; | ||||||||||
| bool transform_feedback_enable(GPUVertBuf *buf) 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; | ||||||||||
| bool get_push_constant_is_dirty(); | ||||||||||
| void push_constant_bindstate_mark_dirty(bool is_dirty); | ||||||||||
| void vertformat_from_shader(GPUVertFormat *format) const override; | ||||||||||
| /* DEPRECATED: Kept only because of BGL API. (Returning -1 in METAL). */ | ||||||||||
| int program_handle_get() const override | ||||||||||
| { | ||||||||||
| return -1; | ||||||||||
| } | ||||||||||
| bool get_uses_ssbo_vertex_fetch() | ||||||||||
| { | ||||||||||
| return use_ssbo_vertex_fetch_mode_; | ||||||||||
| } | ||||||||||
| MTLPrimitiveType get_ssbo_vertex_fetch_output_prim_type() | ||||||||||
| { | ||||||||||
| return ssbo_vertex_fetch_output_prim_type_; | ||||||||||
| } | ||||||||||
| uint32_t get_ssbo_vertex_fetch_output_num_verts() | ||||||||||
| { | ||||||||||
| return ssbo_vertex_fetch_output_num_verts_; | ||||||||||
| } | ||||||||||
| static int ssbo_vertex_type_to_attr_type(MTLVertexFormat attribute_type); | ||||||||||
| void prepare_ssbo_vertex_fetch_metadata(); | ||||||||||
| /* SSBO Vertex Bindings Utility functions. */ | ||||||||||
| void ssbo_vertex_fetch_bind_attributes_begin(); | ||||||||||
| void ssbo_vertex_fetch_bind_attribute(const MTLSSBOAttribute &ssbo_attr); | ||||||||||
| void ssbo_vertex_fetch_bind_attributes_end(id<MTLRenderCommandEncoder> active_encoder); | ||||||||||
| /* Metal shader properties and source mapping. */ | ||||||||||
| void set_vertex_function_name(NSString *vetex_function_name); | ||||||||||
| void set_fragment_function_name(NSString *fragment_function_name_); | ||||||||||
| void shader_source_from_msl(NSString *input_vertex_source, NSString *input_fragment_source); | ||||||||||
| void set_interface(MTLShaderInterface *interface); | ||||||||||
| MTLRenderPipelineStateInstance *bake_current_pipeline_state(MTLContext *ctx, | ||||||||||
| MTLPrimitiveTopologyClass prim_type); | ||||||||||
| /* Transform Feedback. */ | ||||||||||
| GPUVertBuf *get_transform_feedback_active_buffer(); | ||||||||||
| bool has_transform_feedback_varying(std::string str); | ||||||||||
| private: | ||||||||||
| /* Generate MSL shader from GLSL source. */ | ||||||||||
| bool generate_msl_from_glsl(const shader::ShaderCreateInfo *info); | ||||||||||
| MEM_CXX_CLASS_ALLOC_FUNCS("MTLShader"); | ||||||||||
| }; | ||||||||||
| /* Vertex format conversion. | ||||||||||
| * Determines whether it is possible to resize a vertex attribute type | ||||||||||
| * during input assembly. A conversion is implied by the difference | ||||||||||
| * between the input vertex descriptor (from MTLBatch/MTLImmediate) | ||||||||||
| * and the type specified in the shader source. | ||||||||||
| * | ||||||||||
| * e.g. vec3 to vec4 expansion, or vec4 to vec2 truncation. | ||||||||||
| * Note: Vector expansion will replace empty elements with the values | ||||||||||
| * (0,0,0,1). | ||||||||||
| * | ||||||||||
| * If implicit format resize is not possible, this function | ||||||||||
| * returns false. | ||||||||||
| * | ||||||||||
| * Implicitly supported conversions in Metal are described here: | ||||||||||
| * https://developer.apple.com/documentation/metal/mtlvertexattributedescriptor/1516081-format?language=objc | ||||||||||
| */ | ||||||||||
| inline bool mtl_vertex_format_resize(MTLVertexFormat mtl_format, | ||||||||||
| uint32_t components, | ||||||||||
Not Done Inline ActionsIs there a need for it to be stored? I cannot find any usage of it. fclem: Is there a need for it to be stored? I cannot find any usage of it. | ||||||||||
Not Done Inline ActionsYeah, you're right, this appears to be stored unnecessarily, and if it ever did need to be fetched, it could just be found from the corresponding key in the pso_cache_ lookup. MichaelPW: Yeah, you're right, this appears to be stored unnecessarily, and if it ever did need to be… | ||||||||||
| MTLVertexFormat *r_convertedFormat) | ||||||||||
| { | ||||||||||
Not Done Inline Actions
I could not find any use of this. Is that for shader entry point? fclem: I could not find any use of this. Is that for shader entry point? | ||||||||||
Not Done Inline ActionsThis is a reference to a specialised variant of the vertex/fragment function for the current shader. Storing it here is primarily just a means of keeping a reference to the resource. Such that it can be retained while in use in a PSO, and released when the shader object (and pso_cache_) is destroyed. MichaelPW: This is a reference to a specialised variant of the vertex/fragment function for the current… | ||||||||||
| MTLVertexFormat out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| switch (mtl_format) { | ||||||||||
| /* Char. */ | ||||||||||
| case MTLVertexFormatChar: | ||||||||||
| case MTLVertexFormatChar2: | ||||||||||
| case MTLVertexFormatChar3: | ||||||||||
| case MTLVertexFormatChar4: | ||||||||||
| switch (components) { | ||||||||||
| case 1: | ||||||||||
| out_vert_format = MTLVertexFormatChar; | ||||||||||
| break; | ||||||||||
| case 2: | ||||||||||
| out_vert_format = MTLVertexFormatChar2; | ||||||||||
| break; | ||||||||||
| case 3: | ||||||||||
| out_vert_format = MTLVertexFormatChar3; | ||||||||||
| break; | ||||||||||
| case 4: | ||||||||||
| out_vert_format = MTLVertexFormatChar4; | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| /* Normalized Char. */ | ||||||||||
| case MTLVertexFormatCharNormalized: | ||||||||||
| case MTLVertexFormatChar2Normalized: | ||||||||||
Not Done Inline ActionsThis is interesting. Maybe we should try to remove the root cause. But that' fine for now. fclem: This is interesting. Maybe we should try to remove the root cause. But that' fine for now. | ||||||||||
| case MTLVertexFormatChar3Normalized: | ||||||||||
| case MTLVertexFormatChar4Normalized: | ||||||||||
| switch (components) { | ||||||||||
| case 1: | ||||||||||
| out_vert_format = MTLVertexFormatCharNormalized; | ||||||||||
| break; | ||||||||||
| case 2: | ||||||||||
| out_vert_format = MTLVertexFormatChar2Normalized; | ||||||||||
| break; | ||||||||||
| case 3: | ||||||||||
| out_vert_format = MTLVertexFormatChar3Normalized; | ||||||||||
| break; | ||||||||||
| case 4: | ||||||||||
| out_vert_format = MTLVertexFormatChar4Normalized; | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| /* Unsigned Char. */ | ||||||||||
| case MTLVertexFormatUChar: | ||||||||||
| case MTLVertexFormatUChar2: | ||||||||||
| case MTLVertexFormatUChar3: | ||||||||||
| case MTLVertexFormatUChar4: | ||||||||||
| switch (components) { | ||||||||||
| case 1: | ||||||||||
| out_vert_format = MTLVertexFormatUChar; | ||||||||||
Not Done Inline ActionsWait. We already have gpu::Shader::interface as a public member, so you already have access to it. Why another member with diferent type? I see that you have a owns_interface_ but there don't seem to be any case where this is false. Is Interface sharing planned? fclem: Wait. We already have `gpu::Shader::interface` as a public member, so you already have access… | ||||||||||
Not Done Inline ActionsWhen the backend was first implemented, interface sharing, and external interface creation was supported, though since everything now goes through the GLSL pipeline, rather than using hand-written MSL shaders, this is now no longer needed. It was kept as a convenience to avoid repeated casting. Though I imagine the compiler probably negates any overhead with this. I can remove this. MichaelPW: When the backend was first implemented, interface sharing, and external interface creation was… | ||||||||||
| break; | ||||||||||
| case 2: | ||||||||||
| out_vert_format = MTLVertexFormatUChar2; | ||||||||||
| break; | ||||||||||
| case 3: | ||||||||||
| out_vert_format = MTLVertexFormatUChar3; | ||||||||||
| break; | ||||||||||
| case 4: | ||||||||||
| out_vert_format = MTLVertexFormatUChar4; | ||||||||||
| break; | ||||||||||
| } | ||||||||||
Not Done Inline ActionsIf we can expect compute shader support for the initial version of the Metal backend, I would rather not add this. fclem: If we can expect compute shader support for the initial version of the Metal backend, I would… | ||||||||||
Not Done Inline ActionsCompute support is still something needing attention. It should not be too complicated and should be more straight forward than the existing shader translation. Though I can remove this as soon as that support is in place. Would it be okay to keep this as a stop-gap for the time being, to enable testing while compute features are worked on? MichaelPW: Compute support is still something needing attention. It should not be too complicated and… | ||||||||||
Not Done Inline ActionsOk to keep that for the moment then. fclem: Ok to keep that for the moment then. | ||||||||||
| break; | ||||||||||
| /* Normalized Unsigned char */ | ||||||||||
| case MTLVertexFormatUCharNormalized: | ||||||||||
Not Done Inline ActionsWhen is that false? I think it is better to always assume they are separated. We do require different files with different suffix. fclem: When is that false? I think it is better to always assume they are separated. We do require… | ||||||||||
Not Done Inline ActionsYeah, we can remove this and just always use separate files. This did exist from a time where internal metal vertex/fragment shaders shared a single source. MichaelPW: Yeah, we can remove this and just always use separate files. This did exist from a time where… | ||||||||||
| case MTLVertexFormatUChar2Normalized: | ||||||||||
| case MTLVertexFormatUChar3Normalized: | ||||||||||
| case MTLVertexFormatUChar4Normalized: | ||||||||||
| switch (components) { | ||||||||||
| case 1: | ||||||||||
| out_vert_format = MTLVertexFormatUCharNormalized; | ||||||||||
| break; | ||||||||||
| case 2: | ||||||||||
| out_vert_format = MTLVertexFormatUChar2Normalized; | ||||||||||
| break; | ||||||||||
| case 3: | ||||||||||
| out_vert_format = MTLVertexFormatUChar3Normalized; | ||||||||||
| break; | ||||||||||
| case 4: | ||||||||||
| out_vert_format = MTLVertexFormatUChar4Normalized; | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
Not Done Inline ActionsAll of this is concerning shader creation. I do not see any benefit of keeping them inside the struct that is supposed to be reserved for actual data needed for drawing. Source debugging should go through GPULogParser. You can create a temporary MTLShaderBuilder object that contains all of them. It should get freed after compilation. fclem: All of this is concerning shader creation. I do not see any benefit of keeping them inside the… | ||||||||||
Not Done Inline ActionsYeah this is true, though the shader libraries and function entry point names are required prior to PSO (backend) compilation which happens upon a draw, based on the current rendering state. A shader will potentially have several PSO permutations based on the pipeline state at the time it is used. This compilation is deferred until draw time, as this state is generally unknown up-front. We do not however require any of the original string-based sources however, so I'll place these inside an MTLShaderBuilder struct and free these after a shader is finalized. Will also refactor debugging to use GPULogParser. MichaelPW: Yeah this is true, though the shader libraries and function entry point names are required… | ||||||||||
| /* Short. */ | ||||||||||
| case MTLVertexFormatShort: | ||||||||||
| case MTLVertexFormatShort2: | ||||||||||
| case MTLVertexFormatShort3: | ||||||||||
| case MTLVertexFormatShort4: | ||||||||||
| switch (components) { | ||||||||||
| case 1: | ||||||||||
| out_vert_format = MTLVertexFormatShort; | ||||||||||
Not Done Inline ActionsWould have thought this would be given by the MTLState instead of living inside the MTLShader. fclem: Would have thought this would be given by the `MTLState` instead of living inside the… | ||||||||||
| break; | ||||||||||
| case 2: | ||||||||||
| out_vert_format = MTLVertexFormatShort2; | ||||||||||
| break; | ||||||||||
| case 3: | ||||||||||
Not Done Inline ActionsNot sure if this is also shader creation only. fclem: Not sure if this is also shader creation only. | ||||||||||
Not Done Inline ActionsThis is also required for PSO creation (backend shader compilation). When layered rendering is used, primitive type needs to be explicitly specified. Usually, specifying primitive type explicitly means that each time a different primitive type is used with a given shader, a PSO permutation needs to be compiled. We can instead use "unspecified" which allows this to be any type for most cases, reducing the number of backend compilations. MichaelPW: This is also required for PSO creation (backend shader compilation). When layered rendering is… | ||||||||||
| out_vert_format = MTLVertexFormatShort3; | ||||||||||
| break; | ||||||||||
| case 4: | ||||||||||
| out_vert_format = MTLVertexFormatShort4; | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| /* Normalized Short. */ | ||||||||||
| case MTLVertexFormatShortNormalized: | ||||||||||
| case MTLVertexFormatShort2Normalized: | ||||||||||
| case MTLVertexFormatShort3Normalized: | ||||||||||
| case MTLVertexFormatShort4Normalized: | ||||||||||
| switch (components) { | ||||||||||
| case 1: | ||||||||||
| out_vert_format = MTLVertexFormatShortNormalized; | ||||||||||
| break; | ||||||||||
| case 2: | ||||||||||
| out_vert_format = MTLVertexFormatShort2Normalized; | ||||||||||
| break; | ||||||||||
| case 3: | ||||||||||
| out_vert_format = MTLVertexFormatShort3Normalized; | ||||||||||
| break; | ||||||||||
| case 4: | ||||||||||
| out_vert_format = MTLVertexFormatShort4Normalized; | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| /* Unsigned Short. */ | ||||||||||
| case MTLVertexFormatUShort: | ||||||||||
| case MTLVertexFormatUShort2: | ||||||||||
| case MTLVertexFormatUShort3: | ||||||||||
| case MTLVertexFormatUShort4: | ||||||||||
| switch (components) { | ||||||||||
| case 1: | ||||||||||
| out_vert_format = MTLVertexFormatUShort; | ||||||||||
| break; | ||||||||||
| case 2: | ||||||||||
Not Done Inline ActionsNot sure I understand fully what this entails. I would have though that push constants would fit 1 UBO. Is that for cases where it doesn't fit? fclem: Not sure I understand fully what this entails. I would have though that push constants would… | ||||||||||
Not Done Inline ActionsThis was perhaps over-engineering for cases where push constants (general shader uniforms) may be stored in several different UBOs, to allow optimisations such as only updating/re-binding ones which had been modified. Left this in as UBO's had the Frequency field, which seemed like it could extend to uniforms in future. I.e. if certain classes of uniforms only get updated per scene, then having split UBOs could have the advantage of less data being encoded in the command buffer between similar draw calls. However, as it stands, this code does not have an active use-case. So it can be removed and just replaced with a single data store. As all uniforms (push_constants) currently get written to the first data block only. MichaelPW: This was perhaps over-engineering for cases where push constants (general shader uniforms) may… | ||||||||||
Not Done Inline ActionsJust as a note, the Frequency is only for binding frequency. It is not about update frequency which in almost all cases should be once per frame. If there is an update frequency flag it should be on UBO creation. But we haven't one for the moment. So I agree that you can remove it. fclem: Just as a note, the `Frequency` is only for binding frequency. It is not about update frequency… | ||||||||||
| out_vert_format = MTLVertexFormatUShort2; | ||||||||||
| break; | ||||||||||
| case 3: | ||||||||||
| out_vert_format = MTLVertexFormatUShort3; | ||||||||||
| break; | ||||||||||
| case 4: | ||||||||||
| out_vert_format = MTLVertexFormatUShort4; | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| /* Normalized Unsigned Short. */ | ||||||||||
| case MTLVertexFormatUShortNormalized: | ||||||||||
| case MTLVertexFormatUShort2Normalized: | ||||||||||
| case MTLVertexFormatUShort3Normalized: | ||||||||||
| case MTLVertexFormatUShort4Normalized: | ||||||||||
| switch (components) { | ||||||||||
| case 1: | ||||||||||
| out_vert_format = MTLVertexFormatUShortNormalized; | ||||||||||
| break; | ||||||||||
| case 2: | ||||||||||
| out_vert_format = MTLVertexFormatUShort2Normalized; | ||||||||||
| break; | ||||||||||
| case 3: | ||||||||||
| out_vert_format = MTLVertexFormatUShort3Normalized; | ||||||||||
| break; | ||||||||||
| case 4: | ||||||||||
| out_vert_format = MTLVertexFormatUShort4Normalized; | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| /* Integer. */ | ||||||||||
| case MTLVertexFormatInt: | ||||||||||
| case MTLVertexFormatInt2: | ||||||||||
| case MTLVertexFormatInt3: | ||||||||||
| case MTLVertexFormatInt4: | ||||||||||
| switch (components) { | ||||||||||
| case 1: | ||||||||||
| out_vert_format = MTLVertexFormatInt; | ||||||||||
| break; | ||||||||||
| case 2: | ||||||||||
| out_vert_format = MTLVertexFormatInt2; | ||||||||||
| break; | ||||||||||
| case 3: | ||||||||||
| out_vert_format = MTLVertexFormatInt3; | ||||||||||
| break; | ||||||||||
| case 4: | ||||||||||
| out_vert_format = MTLVertexFormatInt4; | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| /* Unsigned Integer. */ | ||||||||||
| case MTLVertexFormatUInt: | ||||||||||
| case MTLVertexFormatUInt2: | ||||||||||
| case MTLVertexFormatUInt3: | ||||||||||
| case MTLVertexFormatUInt4: | ||||||||||
| switch (components) { | ||||||||||
| case 1: | ||||||||||
| out_vert_format = MTLVertexFormatUInt; | ||||||||||
| break; | ||||||||||
| case 2: | ||||||||||
| out_vert_format = MTLVertexFormatUInt2; | ||||||||||
| break; | ||||||||||
| case 3: | ||||||||||
| out_vert_format = MTLVertexFormatUInt3; | ||||||||||
| break; | ||||||||||
| case 4: | ||||||||||
| out_vert_format = MTLVertexFormatUInt4; | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| /* Half. */ | ||||||||||
Not Done Inline ActionsMembers should be above all functions. fclem: Members should be above all functions. | ||||||||||
| case MTLVertexFormatHalf: | ||||||||||
| case MTLVertexFormatHalf2: | ||||||||||
| case MTLVertexFormatHalf3: | ||||||||||
| case MTLVertexFormatHalf4: | ||||||||||
| switch (components) { | ||||||||||
| case 1: | ||||||||||
| out_vert_format = MTLVertexFormatHalf; | ||||||||||
| break; | ||||||||||
| case 2: | ||||||||||
| out_vert_format = MTLVertexFormatHalf2; | ||||||||||
| break; | ||||||||||
| case 3: | ||||||||||
| out_vert_format = MTLVertexFormatHalf3; | ||||||||||
| break; | ||||||||||
| case 4: | ||||||||||
| out_vert_format = MTLVertexFormatHalf4; | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| /* Float. */ | ||||||||||
| case MTLVertexFormatFloat: | ||||||||||
| case MTLVertexFormatFloat2: | ||||||||||
| case MTLVertexFormatFloat3: | ||||||||||
| case MTLVertexFormatFloat4: | ||||||||||
| switch (components) { | ||||||||||
| case 1: | ||||||||||
| out_vert_format = MTLVertexFormatFloat; | ||||||||||
| break; | ||||||||||
| case 2: | ||||||||||
| out_vert_format = MTLVertexFormatFloat2; | ||||||||||
| break; | ||||||||||
| case 3: | ||||||||||
| out_vert_format = MTLVertexFormatFloat3; | ||||||||||
| break; | ||||||||||
| case 4: | ||||||||||
| out_vert_format = MTLVertexFormatFloat4; | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| /* Other formats */ | ||||||||||
| default: | ||||||||||
| out_vert_format = mtl_format; | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| *r_convertedFormat = out_vert_format; | ||||||||||
| return out_vert_format != MTLVertexFormatInvalid; | ||||||||||
| } | ||||||||||
| /* Returns whether the METAL API can internally convert between the input type of data in the | ||||||||||
| * incoming vertex buffer and the format used by the vertex attribute inside the shader. | ||||||||||
| * | ||||||||||
| * - Returns TRUE if the type can be converted internally, along with returning the appropriate | ||||||||||
| * type to be passed into the MTLVertexAttributeDescriptorPSO. | ||||||||||
| * | ||||||||||
| * - Returns FALSE if the type cannot be converted internally e.g. casting Int4 to Float4. | ||||||||||
| * | ||||||||||
| * If implicit conversion is not possible, then we can fallback to performing manual attribute | ||||||||||
| * conversion using the special attribute read function specialisations in the shader. | ||||||||||
| * These functions selectively convert between types based on the specified vertex | ||||||||||
| * attribute 'GPUVertFetchMode fetch_mode' e.g. GPU_FETCH_INT. | ||||||||||
| */ | ||||||||||
| inline bool mtl_convert_vertex_format(MTLVertexFormat shader_attrib_format, | ||||||||||
| GPUVertCompType component_type, | ||||||||||
| uint32_t component_length, | ||||||||||
| GPUVertFetchMode fetch_mode, | ||||||||||
| MTLVertexFormat *r_convertedFormat) | ||||||||||
| { | ||||||||||
| bool normalized = (fetch_mode == GPU_FETCH_INT_TO_FLOAT_UNIT); | ||||||||||
| MTLVertexFormat out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| switch (component_type) { | ||||||||||
| case GPU_COMP_I8: | ||||||||||
| switch (fetch_mode) { | ||||||||||
| case GPU_FETCH_INT: | ||||||||||
| if (shader_attrib_format == MTLVertexFormatChar || | ||||||||||
| shader_attrib_format == MTLVertexFormatChar2 || | ||||||||||
| shader_attrib_format == MTLVertexFormatChar3 || | ||||||||||
| shader_attrib_format == MTLVertexFormatChar4) { | ||||||||||
| /* No conversion Needed (as type matches) - Just a vector resize if needed. */ | ||||||||||
| bool can_convert = mtl_vertex_format_resize( | ||||||||||
| shader_attrib_format, component_type, &out_vert_format); | ||||||||||
| /* Ensure format resize successful. */ | ||||||||||
| BLI_assert(can_convert); | ||||||||||
| UNUSED_VARS_NDEBUG(can_convert); | ||||||||||
| } | ||||||||||
| else if (shader_attrib_format == MTLVertexFormatInt4 && component_length == 4) { | ||||||||||
| /* Allow type expansion - Shader expects MTLVertexFormatInt4, we can supply a type | ||||||||||
| * with fewer bytes if component count is the same. Sign must also match original type | ||||||||||
| * -- which is not a problem in this case. */ | ||||||||||
| out_vert_format = MTLVertexFormatChar4; | ||||||||||
| } | ||||||||||
| else if (shader_attrib_format == MTLVertexFormatInt3 && component_length == 3) { | ||||||||||
| /* Same as above case for matching length and signage (Len=3)*/ | ||||||||||
| out_vert_format = MTLVertexFormatChar3; | ||||||||||
| } | ||||||||||
| else if (shader_attrib_format == MTLVertexFormatInt2 && component_length == 2) { | ||||||||||
| /* Same as above case for matching length and signage (Len=2)*/ | ||||||||||
| out_vert_format = MTLVertexFormatChar2; | ||||||||||
| } | ||||||||||
| else if (shader_attrib_format == MTLVertexFormatInt && component_length == 1) { | ||||||||||
| /* Same as above case for matching length and signage (Len=1)*/ | ||||||||||
| out_vert_format = MTLVertexFormatChar; | ||||||||||
| } | ||||||||||
| else if (shader_attrib_format == MTLVertexFormatInt && component_length == 4) { | ||||||||||
| /* Special case here, format has been specified as GPU_COMP_U8 with 4 components, which | ||||||||||
| * is equivalent to an Int -- so data will be compatible with the shader interface. */ | ||||||||||
| out_vert_format = MTLVertexFormatInt; | ||||||||||
| } | ||||||||||
| else { | ||||||||||
| BLI_assert_msg(false, | ||||||||||
| "Source vertex data format is either Char, Char2, Char3, Char4 but " | ||||||||||
| "format in shader interface is NOT compatible.\n"); | ||||||||||
| out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| /* Source vertex data is integer type, but shader interface type is floating point. | ||||||||||
| * If the input attribute is specified as normalized, we can convert. */ | ||||||||||
| case GPU_FETCH_FLOAT: | ||||||||||
| case GPU_FETCH_INT_TO_FLOAT: | ||||||||||
| case GPU_FETCH_INT_TO_FLOAT_UNIT: | ||||||||||
| if (normalized) { | ||||||||||
| switch (component_length) { | ||||||||||
| case 1: | ||||||||||
| out_vert_format = MTLVertexFormatCharNormalized; | ||||||||||
| break; | ||||||||||
| case 2: | ||||||||||
| out_vert_format = MTLVertexFormatChar2Normalized; | ||||||||||
| break; | ||||||||||
| case 3: | ||||||||||
| out_vert_format = MTLVertexFormatChar3Normalized; | ||||||||||
| break; | ||||||||||
| case 4: | ||||||||||
| out_vert_format = MTLVertexFormatChar4Normalized; | ||||||||||
| break; | ||||||||||
| default: | ||||||||||
| BLI_assert_msg(false, "invalid vertex format"); | ||||||||||
| out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| } | ||||||||||
| } | ||||||||||
| else { | ||||||||||
| /* Cannot convert. */ | ||||||||||
| out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| case GPU_COMP_U8: | ||||||||||
| switch (fetch_mode) { | ||||||||||
| /* Fetching INT: Check backing shader format matches source input. */ | ||||||||||
| case GPU_FETCH_INT: | ||||||||||
| if (shader_attrib_format == MTLVertexFormatUChar || | ||||||||||
| shader_attrib_format == MTLVertexFormatUChar2 || | ||||||||||
| shader_attrib_format == MTLVertexFormatUChar3 || | ||||||||||
| shader_attrib_format == MTLVertexFormatUChar4) { | ||||||||||
| /* No conversion Needed (as type matches) - Just a vector resize if needed. */ | ||||||||||
| bool can_convert = mtl_vertex_format_resize( | ||||||||||
| shader_attrib_format, component_length, &out_vert_format); | ||||||||||
| /* Ensure format resize successful. */ | ||||||||||
| BLI_assert(can_convert); | ||||||||||
| UNUSED_VARS_NDEBUG(can_convert); | ||||||||||
| /* TODO(Metal): Add other format conversions if needed. Currently no attributes hit | ||||||||||
| * this path. */ | ||||||||||
| } | ||||||||||
| else if (shader_attrib_format == MTLVertexFormatUInt4 && component_length == 4) { | ||||||||||
| /* Allow type expansion - Shader expects MTLVertexFormatUInt4, we can supply a type | ||||||||||
| * with fewer bytes if component count is the same. */ | ||||||||||
| out_vert_format = MTLVertexFormatUChar4; | ||||||||||
| } | ||||||||||
| else if (shader_attrib_format == MTLVertexFormatUInt3 && component_length == 3) { | ||||||||||
| /* Same as above case for matching length and signage (Len=3)*/ | ||||||||||
| out_vert_format = MTLVertexFormatUChar3; | ||||||||||
| } | ||||||||||
| else if (shader_attrib_format == MTLVertexFormatUInt2 && component_length == 2) { | ||||||||||
| /* Same as above case for matching length and signage (Len=2)*/ | ||||||||||
| out_vert_format = MTLVertexFormatUChar2; | ||||||||||
| } | ||||||||||
| else if (shader_attrib_format == MTLVertexFormatUInt && component_length == 1) { | ||||||||||
| /* Same as above case for matching length and signage (Len=1)*/ | ||||||||||
| out_vert_format = MTLVertexFormatUChar; | ||||||||||
| } | ||||||||||
| else if (shader_attrib_format == MTLVertexFormatInt && component_length == 4) { | ||||||||||
| /* Special case here, format has been specified as GPU_COMP_U8 with 4 components, which | ||||||||||
| * is equivalent to an Int-- so data will be compatible with shader interface. */ | ||||||||||
| out_vert_format = MTLVertexFormatInt; | ||||||||||
| } | ||||||||||
| else if (shader_attrib_format == MTLVertexFormatUInt && component_length == 4) { | ||||||||||
| /* Special case here, format has been specified as GPU_COMP_U8 with 4 components, which | ||||||||||
| *is equivalent to a UInt-- so data will be compatible with shader interface. */ | ||||||||||
| out_vert_format = MTLVertexFormatUInt; | ||||||||||
| } | ||||||||||
| else { | ||||||||||
| BLI_assert_msg(false, | ||||||||||
| "Source vertex data format is either UChar, UChar2, UChar3, UChar4 but " | ||||||||||
| "format in shader interface is NOT compatible.\n"); | ||||||||||
| out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| /* Source vertex data is integral type, but shader interface type is floating point. | ||||||||||
| * If the input attribute is specified as normalized, we can convert. */ | ||||||||||
| case GPU_FETCH_FLOAT: | ||||||||||
| case GPU_FETCH_INT_TO_FLOAT: | ||||||||||
| case GPU_FETCH_INT_TO_FLOAT_UNIT: | ||||||||||
| if (normalized) { | ||||||||||
| switch (component_length) { | ||||||||||
| case 1: | ||||||||||
| out_vert_format = MTLVertexFormatUCharNormalized; | ||||||||||
| break; | ||||||||||
| case 2: | ||||||||||
| out_vert_format = MTLVertexFormatUChar2Normalized; | ||||||||||
| break; | ||||||||||
| case 3: | ||||||||||
| out_vert_format = MTLVertexFormatUChar3Normalized; | ||||||||||
| break; | ||||||||||
| case 4: | ||||||||||
| out_vert_format = MTLVertexFormatUChar4Normalized; | ||||||||||
| break; | ||||||||||
| default: | ||||||||||
| BLI_assert_msg(false, "invalid vertex format"); | ||||||||||
| out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| } | ||||||||||
| } | ||||||||||
| else { | ||||||||||
| /* Cannot convert. */ | ||||||||||
| out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| case GPU_COMP_I16: | ||||||||||
| switch (fetch_mode) { | ||||||||||
| case GPU_FETCH_INT: | ||||||||||
| if (shader_attrib_format == MTLVertexFormatShort || | ||||||||||
| shader_attrib_format == MTLVertexFormatShort2 || | ||||||||||
| shader_attrib_format == MTLVertexFormatShort3 || | ||||||||||
| shader_attrib_format == MTLVertexFormatShort4) { | ||||||||||
| /* No conversion Needed (as type matches) - Just a vector resize if needed. */ | ||||||||||
| bool can_convert = mtl_vertex_format_resize( | ||||||||||
| shader_attrib_format, component_length, &out_vert_format); | ||||||||||
| /* Ensure conversion successful. */ | ||||||||||
| BLI_assert(can_convert); | ||||||||||
| UNUSED_VARS_NDEBUG(can_convert); | ||||||||||
| } | ||||||||||
| else { | ||||||||||
| BLI_assert_msg(false, | ||||||||||
| "Source vertex data format is either Short, Short2, Short3, Short4 but " | ||||||||||
| "format in shader interface is NOT compatible.\n"); | ||||||||||
| out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| /* Source vertex data is integral type, but shader interface type is floating point. | ||||||||||
| * If the input attribute is specified as normalized, we can convert. */ | ||||||||||
| case GPU_FETCH_FLOAT: | ||||||||||
| case GPU_FETCH_INT_TO_FLOAT: | ||||||||||
| case GPU_FETCH_INT_TO_FLOAT_UNIT: | ||||||||||
| if (normalized) { | ||||||||||
| switch (component_length) { | ||||||||||
| case 1: | ||||||||||
| out_vert_format = MTLVertexFormatShortNormalized; | ||||||||||
| break; | ||||||||||
| case 2: | ||||||||||
| out_vert_format = MTLVertexFormatShort2Normalized; | ||||||||||
| break; | ||||||||||
| case 3: | ||||||||||
| out_vert_format = MTLVertexFormatShort3Normalized; | ||||||||||
| break; | ||||||||||
| case 4: | ||||||||||
| out_vert_format = MTLVertexFormatShort4Normalized; | ||||||||||
| break; | ||||||||||
| default: | ||||||||||
| BLI_assert_msg(false, "invalid vertex format"); | ||||||||||
| out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| } | ||||||||||
| } | ||||||||||
| else { | ||||||||||
| /* Cannot convert. */ | ||||||||||
| out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| case GPU_COMP_U16: | ||||||||||
| switch (fetch_mode) { | ||||||||||
| case GPU_FETCH_INT: | ||||||||||
| if (shader_attrib_format == MTLVertexFormatUShort || | ||||||||||
| shader_attrib_format == MTLVertexFormatUShort2 || | ||||||||||
| shader_attrib_format == MTLVertexFormatUShort3 || | ||||||||||
| shader_attrib_format == MTLVertexFormatUShort4) { | ||||||||||
| /* No conversion Needed (as type matches) - Just a vector resize if needed. */ | ||||||||||
| bool can_convert = mtl_vertex_format_resize( | ||||||||||
| shader_attrib_format, component_length, &out_vert_format); | ||||||||||
| /* Ensure format resize successful. */ | ||||||||||
| BLI_assert(can_convert); | ||||||||||
| UNUSED_VARS_NDEBUG(can_convert); | ||||||||||
| } | ||||||||||
| else { | ||||||||||
| BLI_assert_msg(false, | ||||||||||
| "Source vertex data format is either UShort, UShort2, UShort3, UShort4 " | ||||||||||
| "but format in shader interface is NOT compatible.\n"); | ||||||||||
| out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| /* Source vertex data is integral type, but shader interface type is floating point. | ||||||||||
| * If the input attribute is specified as normalized, we can convert. */ | ||||||||||
| case GPU_FETCH_FLOAT: | ||||||||||
| case GPU_FETCH_INT_TO_FLOAT: | ||||||||||
| case GPU_FETCH_INT_TO_FLOAT_UNIT: | ||||||||||
| if (normalized) { | ||||||||||
| switch (component_length) { | ||||||||||
| case 1: | ||||||||||
| out_vert_format = MTLVertexFormatUShortNormalized; | ||||||||||
| break; | ||||||||||
| case 2: | ||||||||||
| out_vert_format = MTLVertexFormatUShort2Normalized; | ||||||||||
| break; | ||||||||||
| case 3: | ||||||||||
| out_vert_format = MTLVertexFormatUShort3Normalized; | ||||||||||
| break; | ||||||||||
| case 4: | ||||||||||
| out_vert_format = MTLVertexFormatUShort4Normalized; | ||||||||||
| break; | ||||||||||
| default: | ||||||||||
| BLI_assert_msg(false, "invalid vertex format"); | ||||||||||
| out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| } | ||||||||||
| } | ||||||||||
| else { | ||||||||||
| /* Cannot convert. */ | ||||||||||
| out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| case GPU_COMP_I32: | ||||||||||
| switch (fetch_mode) { | ||||||||||
| case GPU_FETCH_INT: | ||||||||||
| if (shader_attrib_format == MTLVertexFormatInt || | ||||||||||
| shader_attrib_format == MTLVertexFormatInt2 || | ||||||||||
| shader_attrib_format == MTLVertexFormatInt3 || | ||||||||||
| shader_attrib_format == MTLVertexFormatInt4) { | ||||||||||
| /* No conversion Needed (as type matches) - Just a vector resize if needed. */ | ||||||||||
| bool can_convert = mtl_vertex_format_resize( | ||||||||||
| shader_attrib_format, component_length, &out_vert_format); | ||||||||||
| /* Verify conversion successful. */ | ||||||||||
| BLI_assert(can_convert); | ||||||||||
| UNUSED_VARS_NDEBUG(can_convert); | ||||||||||
| } | ||||||||||
| else { | ||||||||||
| BLI_assert_msg(false, | ||||||||||
| "Source vertex data format is either Int, Int2, Int3, Int4 but format " | ||||||||||
| "in shader interface is NOT compatible.\n"); | ||||||||||
| out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| case GPU_FETCH_FLOAT: | ||||||||||
| case GPU_FETCH_INT_TO_FLOAT: | ||||||||||
| case GPU_FETCH_INT_TO_FLOAT_UNIT: | ||||||||||
| /* Unfortunately we cannot implicitly convert between Int and Float in METAL. */ | ||||||||||
| out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| case GPU_COMP_U32: | ||||||||||
| switch (fetch_mode) { | ||||||||||
| case GPU_FETCH_INT: | ||||||||||
| if (shader_attrib_format == MTLVertexFormatUInt || | ||||||||||
| shader_attrib_format == MTLVertexFormatUInt2 || | ||||||||||
| shader_attrib_format == MTLVertexFormatUInt3 || | ||||||||||
| shader_attrib_format == MTLVertexFormatUInt4) { | ||||||||||
| /* No conversion Needed (as type matches) - Just a vector resize if needed. */ | ||||||||||
| bool can_convert = mtl_vertex_format_resize( | ||||||||||
| shader_attrib_format, component_length, &out_vert_format); | ||||||||||
| /* Verify conversion successful. */ | ||||||||||
| BLI_assert(can_convert); | ||||||||||
| UNUSED_VARS_NDEBUG(can_convert); | ||||||||||
| } | ||||||||||
| else { | ||||||||||
| BLI_assert_msg(false, | ||||||||||
| "Source vertex data format is either UInt, UInt2, UInt3, UInt4 but " | ||||||||||
| "format in shader interface is NOT compatible.\n"); | ||||||||||
| out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| case GPU_FETCH_FLOAT: | ||||||||||
| case GPU_FETCH_INT_TO_FLOAT: | ||||||||||
| case GPU_FETCH_INT_TO_FLOAT_UNIT: | ||||||||||
| /* Unfortunately we cannot convert between UInt and Float in METAL */ | ||||||||||
| out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| case GPU_COMP_F32: | ||||||||||
| switch (fetch_mode) { | ||||||||||
| /* Source data is float. This will be compatible | ||||||||||
| * if type specified in shader is also float. */ | ||||||||||
| case GPU_FETCH_FLOAT: | ||||||||||
| case GPU_FETCH_INT_TO_FLOAT: | ||||||||||
| case GPU_FETCH_INT_TO_FLOAT_UNIT: | ||||||||||
| if (shader_attrib_format == MTLVertexFormatFloat || | ||||||||||
| shader_attrib_format == MTLVertexFormatFloat2 || | ||||||||||
| shader_attrib_format == MTLVertexFormatFloat3 || | ||||||||||
| shader_attrib_format == MTLVertexFormatFloat4) { | ||||||||||
| /* No conversion Needed (as type matches) - Just a vector resize, if needed. */ | ||||||||||
| bool can_convert = mtl_vertex_format_resize( | ||||||||||
| shader_attrib_format, component_length, &out_vert_format); | ||||||||||
| /* Verify conversion successful. */ | ||||||||||
| BLI_assert(can_convert); | ||||||||||
| UNUSED_VARS_NDEBUG(can_convert); | ||||||||||
| } | ||||||||||
| else { | ||||||||||
| BLI_assert_msg(false, | ||||||||||
| "Source vertex data format is either Float, Float2, Float3, Float4 but " | ||||||||||
| "format in shader interface is NOT compatible.\n"); | ||||||||||
| out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| case GPU_FETCH_INT: | ||||||||||
| /* Unfortunately we cannot convert between Float and Int implicitly in METAL. */ | ||||||||||
| out_vert_format = MTLVertexFormatInvalid; | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| break; | ||||||||||
| case GPU_COMP_I10: | ||||||||||
| out_vert_format = MTLVertexFormatInt1010102Normalized; | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| *r_convertedFormat = out_vert_format; | ||||||||||
| return (out_vert_format != MTLVertexFormatInvalid); | ||||||||||
| } | ||||||||||
| inline uint comp_count_from_vert_format(MTLVertexFormat vert_format) | ||||||||||
| { | ||||||||||
| switch (vert_format) { | ||||||||||
| case MTLVertexFormatFloat: | ||||||||||
| case MTLVertexFormatInt: | ||||||||||
| case MTLVertexFormatUInt: | ||||||||||
| case MTLVertexFormatShort: | ||||||||||
| case MTLVertexFormatUChar: | ||||||||||
| case MTLVertexFormatUCharNormalized: | ||||||||||
| return 1; | ||||||||||
| case MTLVertexFormatUChar2: | ||||||||||
| case MTLVertexFormatUInt2: | ||||||||||
| case MTLVertexFormatFloat2: | ||||||||||
| case MTLVertexFormatInt2: | ||||||||||
| case MTLVertexFormatUChar2Normalized: | ||||||||||
| return 2; | ||||||||||
| case MTLVertexFormatUChar3: | ||||||||||
| case MTLVertexFormatUInt3: | ||||||||||
| case MTLVertexFormatFloat3: | ||||||||||
| case MTLVertexFormatInt3: | ||||||||||
| case MTLVertexFormatShort3Normalized: | ||||||||||
| case MTLVertexFormatUChar3Normalized: | ||||||||||
| return 3; | ||||||||||
| case MTLVertexFormatUChar4: | ||||||||||
| case MTLVertexFormatFloat4: | ||||||||||
| case MTLVertexFormatUInt4: | ||||||||||
| case MTLVertexFormatInt4: | ||||||||||
| case MTLVertexFormatUChar4Normalized: | ||||||||||
| case MTLVertexFormatInt1010102Normalized: | ||||||||||
| default: | ||||||||||
| BLI_assert_msg(false, "Unrecognised attribute type. Add types to switch as needed."); | ||||||||||
| return 0; | ||||||||||
| } | ||||||||||
| } | ||||||||||
| inline GPUVertFetchMode fetchmode_from_vert_format(MTLVertexFormat vert_format) | ||||||||||
| { | ||||||||||
| switch (vert_format) { | ||||||||||
| case MTLVertexFormatFloat: | ||||||||||
| case MTLVertexFormatFloat2: | ||||||||||
| case MTLVertexFormatFloat3: | ||||||||||
| case MTLVertexFormatFloat4: | ||||||||||
| return GPU_FETCH_FLOAT; | ||||||||||
| case MTLVertexFormatUChar: | ||||||||||
| case MTLVertexFormatUChar2: | ||||||||||
| case MTLVertexFormatUChar3: | ||||||||||
| case MTLVertexFormatUChar4: | ||||||||||
| case MTLVertexFormatChar: | ||||||||||
| case MTLVertexFormatChar2: | ||||||||||
| case MTLVertexFormatChar3: | ||||||||||
| case MTLVertexFormatChar4: | ||||||||||
| case MTLVertexFormatUShort: | ||||||||||
| case MTLVertexFormatUShort2: | ||||||||||
| case MTLVertexFormatUShort3: | ||||||||||
| case MTLVertexFormatUShort4: | ||||||||||
| case MTLVertexFormatShort: | ||||||||||
| case MTLVertexFormatShort2: | ||||||||||
| case MTLVertexFormatShort3: | ||||||||||
| case MTLVertexFormatShort4: | ||||||||||
| case MTLVertexFormatUInt: | ||||||||||
| case MTLVertexFormatUInt2: | ||||||||||
| case MTLVertexFormatUInt3: | ||||||||||
| case MTLVertexFormatUInt4: | ||||||||||
| case MTLVertexFormatInt: | ||||||||||
| case MTLVertexFormatInt2: | ||||||||||
| case MTLVertexFormatInt3: | ||||||||||
| case MTLVertexFormatInt4: | ||||||||||
| return GPU_FETCH_INT; | ||||||||||
| case MTLVertexFormatUCharNormalized: | ||||||||||
| case MTLVertexFormatUChar2Normalized: | ||||||||||
| case MTLVertexFormatUChar3Normalized: | ||||||||||
| case MTLVertexFormatUChar4Normalized: | ||||||||||
| case MTLVertexFormatCharNormalized: | ||||||||||
| case MTLVertexFormatChar2Normalized: | ||||||||||
| case MTLVertexFormatChar3Normalized: | ||||||||||
| case MTLVertexFormatChar4Normalized: | ||||||||||
| case MTLVertexFormatUShortNormalized: | ||||||||||
| case MTLVertexFormatUShort2Normalized: | ||||||||||
| case MTLVertexFormatUShort3Normalized: | ||||||||||
| case MTLVertexFormatUShort4Normalized: | ||||||||||
| case MTLVertexFormatShortNormalized: | ||||||||||
| case MTLVertexFormatShort2Normalized: | ||||||||||
| case MTLVertexFormatShort3Normalized: | ||||||||||
| case MTLVertexFormatShort4Normalized: | ||||||||||
| case MTLVertexFormatInt1010102Normalized: | ||||||||||
| return GPU_FETCH_INT_TO_FLOAT_UNIT; | ||||||||||
| default: | ||||||||||
| BLI_assert_msg(false, "Unrecognised attribute type. Add types to switch as needed."); | ||||||||||
| return GPU_FETCH_FLOAT; | ||||||||||
| } | ||||||||||
| } | ||||||||||
| inline GPUVertCompType comp_type_from_vert_format(MTLVertexFormat vert_format) | ||||||||||
| { | ||||||||||
| switch (vert_format) { | ||||||||||
| case MTLVertexFormatUChar: | ||||||||||
| case MTLVertexFormatUChar2: | ||||||||||
| case MTLVertexFormatUChar3: | ||||||||||
| case MTLVertexFormatUChar4: | ||||||||||
| case MTLVertexFormatUCharNormalized: | ||||||||||
| case MTLVertexFormatUChar2Normalized: | ||||||||||
| case MTLVertexFormatUChar3Normalized: | ||||||||||
| case MTLVertexFormatUChar4Normalized: | ||||||||||
| return GPU_COMP_U8; | ||||||||||
| case MTLVertexFormatChar: | ||||||||||
| case MTLVertexFormatChar2: | ||||||||||
| case MTLVertexFormatChar3: | ||||||||||
| case MTLVertexFormatChar4: | ||||||||||
| case MTLVertexFormatCharNormalized: | ||||||||||
| case MTLVertexFormatChar2Normalized: | ||||||||||
| case MTLVertexFormatChar3Normalized: | ||||||||||
| case MTLVertexFormatChar4Normalized: | ||||||||||
| return GPU_COMP_I8; | ||||||||||
| case MTLVertexFormatShort: | ||||||||||
| case MTLVertexFormatShort2: | ||||||||||
| case MTLVertexFormatShort3: | ||||||||||
| case MTLVertexFormatShort4: | ||||||||||
| case MTLVertexFormatShortNormalized: | ||||||||||
| case MTLVertexFormatShort2Normalized: | ||||||||||
| case MTLVertexFormatShort3Normalized: | ||||||||||
| case MTLVertexFormatShort4Normalized: | ||||||||||
| return GPU_COMP_I16; | ||||||||||
| case MTLVertexFormatUShort: | ||||||||||
| case MTLVertexFormatUShort2: | ||||||||||
| case MTLVertexFormatUShort3: | ||||||||||
| case MTLVertexFormatUShort4: | ||||||||||
| case MTLVertexFormatUShortNormalized: | ||||||||||
| case MTLVertexFormatUShort2Normalized: | ||||||||||
| case MTLVertexFormatUShort3Normalized: | ||||||||||
| case MTLVertexFormatUShort4Normalized: | ||||||||||
| return GPU_COMP_U16; | ||||||||||
| case MTLVertexFormatInt: | ||||||||||
| case MTLVertexFormatInt2: | ||||||||||
| case MTLVertexFormatInt3: | ||||||||||
| case MTLVertexFormatInt4: | ||||||||||
| return GPU_COMP_I32; | ||||||||||
| case MTLVertexFormatUInt: | ||||||||||
| case MTLVertexFormatUInt2: | ||||||||||
| case MTLVertexFormatUInt3: | ||||||||||
| case MTLVertexFormatUInt4: | ||||||||||
| return GPU_COMP_U32; | ||||||||||
| case MTLVertexFormatFloat: | ||||||||||
| case MTLVertexFormatFloat2: | ||||||||||
| case MTLVertexFormatFloat3: | ||||||||||
| case MTLVertexFormatFloat4: | ||||||||||
| return GPU_COMP_F32; | ||||||||||
| case MTLVertexFormatInt1010102Normalized: | ||||||||||
| return GPU_COMP_I10; | ||||||||||
| default: | ||||||||||
| BLI_assert_msg(false, "Unrecognised attribute type. Add types to switch as needed."); | ||||||||||
| return GPU_COMP_F32; | ||||||||||
| } | ||||||||||
| } | ||||||||||
| } // namespace blender::gpu | ||||||||||
Could we use a shared header for these? similar to draw_shader_shared.h.