/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2022 Blender Foundation. */ #pragma once /** \file * \ingroup draw * * Passes record draw commands. Commands are executed only when a pass is submitted for execution. * * `PassMain`: * Should be used on heavy load passes such as ones that may contain scene objects. Draw call * submission is optimized for large number of draw calls. But has a significant overhead per * #Pass. Use many #PassSub along with a main #Pass to reduce the overhead and allow groupings of * commands. \note The draw call order inside a batch of multiple draw with the exact same state is * not guaranteed and is not even deterministic. Use a #PassSimple or #PassSortable if ordering is * needed. Custom vertex count and custom first vertex will effectively disable batching. * * `PassSimple`: * Does not have the overhead of #PassMain but does not have the culling and batching optimization. * It should be used for passes that needs a few commands or that needs guaranteed draw call order. * * `Pass::Sub`: * A lightweight #Pass that lives inside a main #Pass. It can only be created from #Pass.sub() * and is auto managed. This mean it can be created, filled and thrown away. A #PassSub reference * is valid until the next #Pass.init() of the parent pass. Commands recorded inside a #PassSub are * inserted inside the parent #Pass where the sub have been created during submission. * * `PassSortable`: * This is a sort of `PassMain` augmented with a per sub-pass sorting value. They can't directly * contain draw command, everything needs to be inside sub-passes. Sub-passes are automatically * sorted before submission. * * \note A pass can be recorded once and resubmitted any number of time. This can be a good * optimization for passes that are always the same for each frame. The only thing to be aware of * is the life time of external resources. If a pass contains draw-calls with non default * #ResourceHandle (not 0) or a reference to any non static resources * (#GPUBatch, #PushConstant ref, #ResourceBind ref) it will have to be re-recorded * if any of these reference becomes invalid. */ #include "BKE_image.h" #include "BLI_vector.hh" #include "DRW_gpu_wrapper.hh" #include "GPU_debug.h" #include "GPU_material.h" #include "draw_command.hh" #include "draw_handle.hh" #include "draw_manager.hh" #include "draw_pass.hh" #include "draw_shader_shared.h" #include "draw_state.h" #include "intern/gpu_codegen.h" namespace blender::draw { using namespace blender::draw; using namespace blender::draw::command; class Manager; /* -------------------------------------------------------------------- */ /** \name Pass API * \{ */ namespace detail { /** * Special container that never moves allocated items and has fast indexing. */ template class SubPassVector { private: Vector>, 0> blocks_; public: void clear() { blocks_.clear(); } int64_t append_and_get_index(T &&elem) { /* Do not go over the inline size so that existing members never move. */ if (blocks_.is_empty() || blocks_.last()->size() == block_size) { blocks_.append(std::make_unique>()); } return blocks_.last()->append_and_get_index(std::move(elem)) + (blocks_.size() - 1) * block_size; } T &operator[](int64_t index) { return (*blocks_[index / block_size])[index % block_size]; } const T &operator[](int64_t index) const { return (*blocks_[index / block_size])[index % block_size]; } }; /** * Public API of a draw pass. */ template< /** Type of command buffer used to create the draw calls. */ typename DrawCommandBufType> class PassBase { friend Manager; /** Will use texture own sampler state. */ static constexpr eGPUSamplerState sampler_auto = GPU_SAMPLER_MAX; protected: /** Highest level of the command stream. Split command stream in different command types. */ Vector headers_; /** Commands referenced by headers (which contains their types). */ Vector commands_; /* Reference to draw commands buffer. Either own or from parent pass. */ DrawCommandBufType &draw_commands_buf_; /* Reference to sub-pass commands buffer. Either own or from parent pass. */ SubPassVector> &sub_passes_; /** Currently bound shader. Used for interface queries. */ GPUShader *shader_; public: const char *debug_name; PassBase(const char *name, DrawCommandBufType &draw_command_buf, SubPassVector> &sub_passes, GPUShader *shader = nullptr) : draw_commands_buf_(draw_command_buf), sub_passes_(sub_passes), shader_(shader), debug_name(name){}; /** * Reset the pass command pool. * \note Implemented in derived class. Not a virtual function to avoid indirection. Here only for * API readability listing. */ void init(); /** * Create a sub-pass inside this pass. */ PassBase &sub(const char *name); /** * Changes the fixed function pipeline state. * Starts as DRW_STATE_NO_DRAW at the start of a Pass submission. * SubPass inherit previous pass state. * * IMPORTANT: This does not set the stencil mask/reference values. Add a call to state_stencil() * to ensure correct behavior of stencil aware draws. * * TODO(fclem): clip_plane_count should be part of shader state. */ void state_set(DRWState state, int clip_plane_count = 0); /** * Clear the current frame-buffer. */ void clear_color(float4 color); void clear_depth(float depth); void clear_stencil(uint8_t stencil); void clear_depth_stencil(float depth, uint8_t stencil); void clear_color_depth_stencil(float4 color, float depth, uint8_t stencil); /** * Clear each color attachment with different values. Span needs to be appropriately sized. * IMPORTANT: The source is dereference on pass submission. */ void clear_multi(Span colors); /** * Reminders: * - `compare_mask & reference` is what is tested against `compare_mask & stencil_value` * stencil_value being the value stored in the stencil buffer. * - `write-mask & reference` is what gets written if the test condition is fulfilled. * * This will modify the stencil state until another call to this function. * If not specified before any draw-call, these states will be undefined. * * For more information see: * https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkStencilOpState.html */ void state_stencil(uint8_t write_mask, uint8_t reference, uint8_t compare_mask); /** * Bind a shader. Any following bind() or push_constant() call will use its interface. */ void shader_set(GPUShader *shader); /** * Bind a framebuffer. This is equivalent to a deferred GPU_framebuffer_bind() call. * \note Changes the global GPU state (outside of DRW). * \note Capture reference to the framebuffer so it can be initialized later. */ void framebuffer_set(GPUFrameBuffer **framebuffer); /** * Bind a material shader along with its associated resources. Any following bind() or * push_constant() call will use its interface. * IMPORTANT: Assumes material is compiled and can be used (no compilation error). */ void material_set(Manager &manager, GPUMaterial *material); /** * Record a draw call. * \note Setting the count or first to -1 will use the values from the batch. * \note An instance or vertex count of 0 will discard the draw call. It will not be recorded. */ void draw(GPUBatch *batch, uint instance_len = -1, uint vertex_len = -1, uint vertex_first = -1, ResourceHandle handle = {0}); /** * Shorter version for the common case. * \note Implemented in derived class. Not a virtual function to avoid indirection. */ void draw(GPUBatch *batch, ResourceHandle handle); /** * Record a procedural draw call. Geometry is **NOT** source from a GPUBatch. * \note An instance or vertex count of 0 will discard the draw call. It will not be recorded. */ void draw_procedural(GPUPrimType primitive, uint instance_len, uint vertex_len, uint vertex_first = -1, ResourceHandle handle = {0}); /** * Indirect variants. * \note If needed, the resource id need to also be set accordingly in the DrawCommand. */ void draw_indirect(GPUBatch *batch, StorageBuffer &indirect_buffer, ResourceHandle handle = {0}); void draw_procedural_indirect(GPUPrimType primitive, StorageBuffer &indirect_buffer, ResourceHandle handle = {0}); /** * Record a compute dispatch call. */ void dispatch(int3 group_len); void dispatch(int3 *group_len); void dispatch(StorageBuffer &indirect_buffer); /** * Record a barrier call to synchronize arbitrary load/store operation between draw calls. */ void barrier(eGPUBarrier type); /** * Bind a shader resource. * * Reference versions are to be used when the resource might be resize / realloc or even change * between the time it is referenced and the time it is dereferenced for drawing. * * IMPORTANT: Will keep a reference to the data and dereference it upon drawing. Make sure data * still alive until pass submission. * * \note Variations using slot will not query a shader interface and can be used before * binding a shader. */ void bind_image(const char *name, GPUTexture *image); void bind_image(const char *name, GPUTexture **image); void bind_image(int slot, GPUTexture *image); void bind_image(int slot, GPUTexture **image); void bind_texture(const char *name, GPUTexture *texture, eGPUSamplerState state = sampler_auto); void bind_texture(const char *name, GPUTexture **texture, eGPUSamplerState state = sampler_auto); void bind_texture(int slot, GPUTexture *texture, eGPUSamplerState state = sampler_auto); void bind_texture(int slot, GPUTexture **texture, eGPUSamplerState state = sampler_auto); void bind_ssbo(const char *name, GPUStorageBuf *buffer); void bind_ssbo(const char *name, GPUStorageBuf **buffer); void bind_ssbo(int slot, GPUStorageBuf *buffer); void bind_ssbo(int slot, GPUStorageBuf **buffer); void bind_ubo(const char *name, GPUUniformBuf *buffer); void bind_ubo(const char *name, GPUUniformBuf **buffer); void bind_ubo(int slot, GPUUniformBuf *buffer); void bind_ubo(int slot, GPUUniformBuf **buffer); /** * Update a shader constant. * * Reference versions are to be used when the resource might change between the time it is * referenced and the time it is dereferenced for drawing. * * IMPORTANT: Will keep a reference to the data and dereference it upon drawing. Make sure data * still alive until pass submission. * * \note bool reference version is expected to take bool1 reference which is aliased to int. */ void push_constant(const char *name, const float &data); void push_constant(const char *name, const float2 &data); void push_constant(const char *name, const float3 &data); void push_constant(const char *name, const float4 &data); void push_constant(const char *name, const int &data); void push_constant(const char *name, const int2 &data); void push_constant(const char *name, const int3 &data); void push_constant(const char *name, const int4 &data); void push_constant(const char *name, const bool &data); void push_constant(const char *name, const float4x4 &data); void push_constant(const char *name, const float *data, int array_len = 1); void push_constant(const char *name, const float2 *data, int array_len = 1); void push_constant(const char *name, const float3 *data, int array_len = 1); void push_constant(const char *name, const float4 *data, int array_len = 1); void push_constant(const char *name, const int *data, int array_len = 1); void push_constant(const char *name, const int2 *data, int array_len = 1); void push_constant(const char *name, const int3 *data, int array_len = 1); void push_constant(const char *name, const int4 *data, int array_len = 1); void push_constant(const char *name, const float4x4 *data); /** * Turn the pass into a string for inspection. */ std::string serialize(std::string line_prefix = "") const; friend std::ostream &operator<<(std::ostream &stream, const PassBase &pass) { return stream << pass.serialize(); } protected: /** * Internal Helpers */ int push_constant_offset(const char *name); void clear(eGPUFrameBufferBits planes, float4 color, float depth, uint8_t stencil); GPUBatch *procedural_batch_get(GPUPrimType primitive); /** * Return a new command recorded with the given type. */ command::Undetermined &create_command(command::Type type); void submit(command::RecordingState &state) const; }; template class Pass : public detail::PassBase { public: using Sub = detail::PassBase; private: /** Sub-passes referenced by headers. */ SubPassVector> sub_passes_main_; /** Draws are recorded as indirect draws for compatibility with the multi-draw pipeline. */ DrawCommandBufType draw_commands_buf_main_; public: Pass(const char *name) : detail::PassBase(name, draw_commands_buf_main_, sub_passes_main_){}; void init() { this->headers_.clear(); this->commands_.clear(); this->sub_passes_.clear(); this->draw_commands_buf_.clear(); } }; // namespace blender::draw } // namespace detail /** \} */ /* -------------------------------------------------------------------- */ /** \name Pass types * \{ */ /** * Normal pass type. No visibility or draw-call optimization. */ // using PassSimple = detail::Pass; /** * Main pass type. * Optimized for many draw calls and sub-pass. * * IMPORTANT: To be used only for passes containing lots of draw calls since it has a potentially * high overhead due to batching and culling optimizations. */ // using PassMain = detail::Pass; /** * Special pass type for rendering transparent objects. * The base level can only be composed of sub passes that will be ordered by a sorting value. */ class PassSortable : public PassMain { friend Manager; private: /** Sorting value associated with each sub pass. */ Vector sorting_values_; bool sorted_ = false; public: PassSortable(const char *name_) : PassMain(name_){}; void init() { sorting_values_.clear(); sorted_ = false; PassMain::init(); } PassMain::Sub &sub(const char *name, float sorting_value) { int64_t index = sub_passes_.append_and_get_index( PassBase(name, draw_commands_buf_, sub_passes_, shader_)); headers_.append({Type::SubPass, uint(index)}); sorting_values_.append(sorting_value); return sub_passes_[index]; } std::string serialize(std::string line_prefix = "") const { if (sorted_ == false) { const_cast(this)->sort(); } return PassMain::serialize(line_prefix); } protected: void sort() { if (sorted_ == false) { std::sort(headers_.begin(), headers_.end(), [&](Header &a, Header &b) { BLI_assert(a.type == Type::SubPass && b.type == Type::SubPass); float a_val = sorting_values_[a.index]; float b_val = sorting_values_[b.index]; return a_val < b_val || (a_val == b_val && a.index < b.index); }); sorted_ = true; } } }; /** \} */ namespace detail { /* -------------------------------------------------------------------- */ /** \name PassBase Implementation * \{ */ template inline command::Undetermined &PassBase::create_command(command::Type type) { int64_t index = commands_.append_and_get_index({}); headers_.append({type, uint(index)}); return commands_[index]; } template inline void PassBase::clear(eGPUFrameBufferBits planes, float4 color, float depth, uint8_t stencil) { create_command(command::Type::Clear).clear = {uint8_t(planes), stencil, depth, color}; } template inline void PassBase::clear_multi(Span colors) { create_command(command::Type::ClearMulti).clear_multi = {colors}; } template inline GPUBatch *PassBase::procedural_batch_get(GPUPrimType primitive) { switch (primitive) { case GPU_PRIM_POINTS: return drw_cache_procedural_points_get(); case GPU_PRIM_LINES: return drw_cache_procedural_lines_get(); case GPU_PRIM_TRIS: return drw_cache_procedural_triangles_get(); case GPU_PRIM_TRI_STRIP: return drw_cache_procedural_triangle_strips_get(); default: /* Add new one as needed. */ BLI_assert_unreachable(); return nullptr; } } template inline PassBase &PassBase::sub(const char *name) { int64_t index = sub_passes_.append_and_get_index( PassBase(name, draw_commands_buf_, sub_passes_, shader_)); headers_.append({command::Type::SubPass, uint(index)}); return sub_passes_[index]; } template void PassBase::submit(command::RecordingState &state) const { GPU_debug_group_begin(debug_name); for (const command::Header &header : headers_) { switch (header.type) { default: case Type::None: break; case Type::SubPass: sub_passes_[header.index].submit(state); break; case command::Type::FramebufferBind: commands_[header.index].framebuffer_bind.execute(); break; case command::Type::ShaderBind: commands_[header.index].shader_bind.execute(state); break; case command::Type::ResourceBind: commands_[header.index].resource_bind.execute(); break; case command::Type::PushConstant: commands_[header.index].push_constant.execute(state); break; case command::Type::Draw: commands_[header.index].draw.execute(state); break; case command::Type::DrawMulti: commands_[header.index].draw_multi.execute(state); break; case command::Type::DrawIndirect: commands_[header.index].draw_indirect.execute(state); break; case command::Type::Dispatch: commands_[header.index].dispatch.execute(state); break; case command::Type::DispatchIndirect: commands_[header.index].dispatch_indirect.execute(state); break; case command::Type::Barrier: commands_[header.index].barrier.execute(); break; case command::Type::Clear: commands_[header.index].clear.execute(); break; case command::Type::ClearMulti: commands_[header.index].clear_multi.execute(); break; case command::Type::StateSet: commands_[header.index].state_set.execute(state); break; case command::Type::StencilSet: commands_[header.index].stencil_set.execute(); break; } } GPU_debug_group_end(); } template std::string PassBase::serialize(std::string line_prefix) const { std::stringstream ss; ss << line_prefix << "." << debug_name << std::endl; line_prefix += " "; for (const command::Header &header : headers_) { switch (header.type) { default: case Type::None: break; case Type::SubPass: ss << sub_passes_[header.index].serialize(line_prefix); break; case Type::FramebufferBind: ss << line_prefix << commands_[header.index].framebuffer_bind.serialize() << std::endl; break; case Type::ShaderBind: ss << line_prefix << commands_[header.index].shader_bind.serialize() << std::endl; break; case Type::ResourceBind: ss << line_prefix << commands_[header.index].resource_bind.serialize() << std::endl; break; case Type::PushConstant: ss << line_prefix << commands_[header.index].push_constant.serialize() << std::endl; break; case Type::Draw: ss << line_prefix << commands_[header.index].draw.serialize() << std::endl; break; case Type::DrawMulti: ss << commands_[header.index].draw_multi.serialize(line_prefix); break; case Type::DrawIndirect: ss << line_prefix << commands_[header.index].draw_indirect.serialize() << std::endl; break; case Type::Dispatch: ss << line_prefix << commands_[header.index].dispatch.serialize() << std::endl; break; case Type::DispatchIndirect: ss << line_prefix << commands_[header.index].dispatch_indirect.serialize() << std::endl; break; case Type::Barrier: ss << line_prefix << commands_[header.index].barrier.serialize() << std::endl; break; case Type::Clear: ss << line_prefix << commands_[header.index].clear.serialize() << std::endl; break; case Type::ClearMulti: ss << line_prefix << commands_[header.index].clear_multi.serialize() << std::endl; break; case Type::StateSet: ss << line_prefix << commands_[header.index].state_set.serialize() << std::endl; break; case Type::StencilSet: ss << line_prefix << commands_[header.index].stencil_set.serialize() << std::endl; break; } } return ss.str(); } /** \} */ /* -------------------------------------------------------------------- */ /** \name Draw calls * \{ */ template inline void PassBase::draw( GPUBatch *batch, uint instance_len, uint vertex_len, uint vertex_first, ResourceHandle handle) { if (instance_len == 0 || vertex_len == 0) { return; } BLI_assert(shader_); draw_commands_buf_.append_draw( headers_, commands_, batch, instance_len, vertex_len, vertex_first, handle); } template inline void PassBase::draw(GPUBatch *batch, ResourceHandle handle) { this->draw(batch, -1, -1, -1, handle); } template inline void PassBase::draw_procedural(GPUPrimType primitive, uint instance_len, uint vertex_len, uint vertex_first, ResourceHandle handle) { this->draw(procedural_batch_get(primitive), instance_len, vertex_len, vertex_first, handle); } /** \} */ /* -------------------------------------------------------------------- */ /** \name Indirect draw calls * \{ */ template inline void PassBase::draw_indirect(GPUBatch *batch, StorageBuffer &indirect_buffer, ResourceHandle handle) { BLI_assert(shader_); create_command(Type::DrawIndirect).draw_indirect = {batch, &indirect_buffer, handle}; } template inline void PassBase::draw_procedural_indirect( GPUPrimType primitive, StorageBuffer &indirect_buffer, ResourceHandle handle) { this->draw_indirect(procedural_batch_get(primitive), indirect_buffer, handle); } /** \} */ /* -------------------------------------------------------------------- */ /** \name Compute Dispatch Implementation * \{ */ template inline void PassBase::dispatch(int3 group_len) { BLI_assert(shader_); create_command(Type::Dispatch).dispatch = {group_len}; } template inline void PassBase::dispatch(int3 *group_len) { BLI_assert(shader_); create_command(Type::Dispatch).dispatch = {group_len}; } template inline void PassBase::dispatch(StorageBuffer &indirect_buffer) { BLI_assert(shader_); create_command(Type::DispatchIndirect).dispatch_indirect = {&indirect_buffer}; } /** \} */ /* -------------------------------------------------------------------- */ /** \name Clear Implementation * \{ */ template inline void PassBase::clear_color(float4 color) { this->clear(GPU_COLOR_BIT, color, 0.0f, 0); } template inline void PassBase::clear_depth(float depth) { this->clear(GPU_DEPTH_BIT, float4(0.0f), depth, 0); } template inline void PassBase::clear_stencil(uint8_t stencil) { this->clear(GPU_STENCIL_BIT, float4(0.0f), 0.0f, stencil); } template inline void PassBase::clear_depth_stencil(float depth, uint8_t stencil) { this->clear(GPU_DEPTH_BIT | GPU_STENCIL_BIT, float4(0.0f), depth, stencil); } template inline void PassBase::clear_color_depth_stencil(float4 color, float depth, uint8_t stencil) { this->clear(GPU_DEPTH_BIT | GPU_STENCIL_BIT | GPU_COLOR_BIT, color, depth, stencil); } /** \} */ /* -------------------------------------------------------------------- */ /** \name Barrier Implementation * \{ */ template inline void PassBase::barrier(eGPUBarrier type) { create_command(Type::Barrier).barrier = {type}; } /** \} */ /* -------------------------------------------------------------------- */ /** \name State Implementation * \{ */ template inline void PassBase::state_set(DRWState state, int clip_plane_count) { /** \note This is for compatibility with the old clip plane API. */ if (clip_plane_count > 0) { state |= DRW_STATE_CLIP_PLANES; } create_command(Type::StateSet).state_set = {state, clip_plane_count}; } template inline void PassBase::state_stencil(uint8_t write_mask, uint8_t reference, uint8_t compare_mask) { create_command(Type::StencilSet).stencil_set = {write_mask, compare_mask, reference}; } template inline void PassBase::shader_set(GPUShader *shader) { shader_ = shader; create_command(Type::ShaderBind).shader_bind = {shader}; } template inline void PassBase::framebuffer_set(GPUFrameBuffer **framebuffer) { create_command(Type::FramebufferBind).framebuffer_bind = {framebuffer}; } template inline void PassBase::material_set(Manager &manager, GPUMaterial *material) { GPUPass *gpupass = GPU_material_get_pass(material); shader_set(GPU_pass_shader_get(gpupass)); /* Bind all textures needed by the material. */ ListBase textures = GPU_material_textures(material); for (GPUMaterialTexture *tex : ListBaseWrapper(textures)) { if (tex->ima) { /* Image */ ImageUser *iuser = tex->iuser_available ? &tex->iuser : nullptr; if (tex->tiled_mapping_name[0]) { GPUTexture *tiles = BKE_image_get_gpu_tiles(tex->ima, iuser, nullptr); manager.acquire_texture(tiles); bind_texture(tex->sampler_name, tiles, (eGPUSamplerState)tex->sampler_state); GPUTexture *tile_map = BKE_image_get_gpu_tilemap(tex->ima, iuser, nullptr); manager.acquire_texture(tile_map); bind_texture(tex->tiled_mapping_name, tile_map, (eGPUSamplerState)tex->sampler_state); } else { GPUTexture *texture = BKE_image_get_gpu_texture(tex->ima, iuser, nullptr); manager.acquire_texture(texture); bind_texture(tex->sampler_name, texture, (eGPUSamplerState)tex->sampler_state); } } else if (tex->colorband) { /* Color Ramp */ bind_texture(tex->sampler_name, *tex->colorband); } } GPUUniformBuf *ubo = GPU_material_uniform_buffer_get(material); if (ubo != nullptr) { bind_ubo(GPU_UBO_BLOCK_NAME, ubo); } } /** \} */ /* -------------------------------------------------------------------- */ /** \name Resource bind Implementation * \{ */ template inline int PassBase::push_constant_offset(const char *name) { return GPU_shader_get_uniform(shader_, name); } template inline void PassBase::bind_ssbo(const char *name, GPUStorageBuf *buffer) { this->bind_ssbo(GPU_shader_get_ssbo(shader_, name), buffer); } template inline void PassBase::bind_ubo(const char *name, GPUUniformBuf *buffer) { this->bind_ubo(GPU_shader_get_uniform_block_binding(shader_, name), buffer); } template inline void PassBase::bind_texture(const char *name, GPUTexture *texture, eGPUSamplerState state) { this->bind_texture(GPU_shader_get_texture_binding(shader_, name), texture, state); } template inline void PassBase::bind_image(const char *name, GPUTexture *image) { this->bind_image(GPU_shader_get_texture_binding(shader_, name), image); } template inline void PassBase::bind_ssbo(int slot, GPUStorageBuf *buffer) { create_command(Type::ResourceBind).resource_bind = {slot, buffer}; } template inline void PassBase::bind_ubo(int slot, GPUUniformBuf *buffer) { create_command(Type::ResourceBind).resource_bind = {slot, buffer}; } template inline void PassBase::bind_texture(int slot, GPUTexture *texture, eGPUSamplerState state) { create_command(Type::ResourceBind).resource_bind = {slot, texture, state}; } template inline void PassBase::bind_image(int slot, GPUTexture *image) { create_command(Type::ResourceBind).resource_bind = {slot, as_image(image)}; } template inline void PassBase::bind_ssbo(const char *name, GPUStorageBuf **buffer) { this->bind_ssbo(GPU_shader_get_ssbo(shader_, name), buffer); } template inline void PassBase::bind_ubo(const char *name, GPUUniformBuf **buffer) { this->bind_ubo(GPU_shader_get_uniform_block_binding(shader_, name), buffer); } template inline void PassBase::bind_texture(const char *name, GPUTexture **texture, eGPUSamplerState state) { this->bind_texture(GPU_shader_get_texture_binding(shader_, name), texture, state); } template inline void PassBase::bind_image(const char *name, GPUTexture **image) { this->bind_image(GPU_shader_get_texture_binding(shader_, name), image); } template inline void PassBase::bind_ssbo(int slot, GPUStorageBuf **buffer) { create_command(Type::ResourceBind).resource_bind = {slot, buffer}; } template inline void PassBase::bind_ubo(int slot, GPUUniformBuf **buffer) { create_command(Type::ResourceBind).resource_bind = {slot, buffer}; } template inline void PassBase::bind_texture(int slot, GPUTexture **texture, eGPUSamplerState state) { create_command(Type::ResourceBind).resource_bind = {slot, texture, state}; } template inline void PassBase::bind_image(int slot, GPUTexture **image) { create_command(Type::ResourceBind).resource_bind = {slot, as_image(image)}; } /** \} */ /* -------------------------------------------------------------------- */ /** \name Push Constant Implementation * \{ */ template inline void PassBase::push_constant(const char *name, const float &data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } template inline void PassBase::push_constant(const char *name, const float2 &data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } template inline void PassBase::push_constant(const char *name, const float3 &data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } template inline void PassBase::push_constant(const char *name, const float4 &data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } template inline void PassBase::push_constant(const char *name, const int &data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } template inline void PassBase::push_constant(const char *name, const int2 &data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } template inline void PassBase::push_constant(const char *name, const int3 &data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } template inline void PassBase::push_constant(const char *name, const int4 &data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } template inline void PassBase::push_constant(const char *name, const bool &data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } template inline void PassBase::push_constant(const char *name, const float *data, int array_len) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len}; } template inline void PassBase::push_constant(const char *name, const float2 *data, int array_len) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len}; } template inline void PassBase::push_constant(const char *name, const float3 *data, int array_len) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len}; } template inline void PassBase::push_constant(const char *name, const float4 *data, int array_len) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len}; } template inline void PassBase::push_constant(const char *name, const int *data, int array_len) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len}; } template inline void PassBase::push_constant(const char *name, const int2 *data, int array_len) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len}; } template inline void PassBase::push_constant(const char *name, const int3 *data, int array_len) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len}; } template inline void PassBase::push_constant(const char *name, const int4 *data, int array_len) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len}; } template inline void PassBase::push_constant(const char *name, const float4x4 *data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } template inline void PassBase::push_constant(const char *name, const float4x4 &data) { /* WORKAROUND: Push 3 consecutive commands to hold the 64 bytes of the float4x4. * This assumes that all commands are always stored in flat array of memory. */ Undetermined commands[3]; PushConstant &cmd = commands[0].push_constant; cmd.location = push_constant_offset(name); cmd.array_len = 1; cmd.comp_len = 16; cmd.type = PushConstant::Type::FloatValue; /* Copy overrides the next 2 commands. We append them as Type::None to not evaluate them. */ *reinterpret_cast(&cmd.float4_value) = data; create_command(Type::PushConstant) = commands[0]; create_command(Type::None) = commands[1]; create_command(Type::None) = commands[2]; } /** \} */ } // namespace detail } // namespace blender::draw