diff options
Diffstat (limited to 'source/blender/draw/intern/draw_pass.hh')
-rw-r--r-- | source/blender/draw/intern/draw_pass.hh | 1011 |
1 files changed, 1011 insertions, 0 deletions
diff --git a/source/blender/draw/intern/draw_pass.hh b/source/blender/draw/intern/draw_pass.hh new file mode 100644 index 00000000000..24dfdd1b97b --- /dev/null +++ b/source/blender/draw/intern/draw_pass.hh @@ -0,0 +1,1011 @@ +/* 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. \note As of now, it is also quite limited in the type of draw command it can record + * (no custom vertex count, no custom first vertex). + * + * `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<T>::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<typename T, + /** Numbers of element of type T to allocate together. */ + int64_t block_size = 16> +class SubPassVector { + private: + Vector<std::unique_ptr<Vector<T, block_size>>, 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<Vector<T, block_size>>()); + } + 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<command::Header, 0> headers_; + /** Commands referenced by headers (which contains their types). */ + Vector<command::Undetermined, 0> 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<PassBase<DrawCommandBufType>> &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<PassBase<DrawCommandBufType>> &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<DrawCommandBufType> &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. + */ + void state_set(DRWState state); + + /** + * 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); + + /** + * 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 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<DrawCommand, true> &indirect_buffer, + ResourceHandle handle = {0}); + void draw_procedural_indirect(GPUPrimType primitive, + StorageBuffer<DrawCommand, true> &indirect_buffer, + ResourceHandle handle = {0}); + + /** + * Record a compute dispatch call. + */ + void dispatch(int3 group_len); + void dispatch(int3 *group_len); + void dispatch(StorageBuffer<DispatchCommand> &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<typename DrawCommandBufType> class Pass : public detail::PassBase<DrawCommandBufType> { + public: + using Sub = detail::PassBase<DrawCommandBufType>; + + private: + /** Sub-passes referenced by headers. */ + SubPassVector<detail::PassBase<DrawCommandBufType>> 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<DrawCommandBufType>(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<DrawCommandBuf>; + +/** + * 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<DrawMultiBuf>; + +/** + * 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<float> 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<PassSortable *>(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<class T> inline command::Undetermined &PassBase<T>::create_command(command::Type type) +{ + int64_t index = commands_.append_and_get_index({}); + headers_.append({type, uint(index)}); + return commands_[index]; +} + +template<class T> +inline void PassBase<T>::clear(eGPUFrameBufferBits planes, + float4 color, + float depth, + uint8_t stencil) +{ + create_command(command::Type::Clear).clear = {uint8_t(planes), stencil, depth, color}; +} + +template<class T> inline GPUBatch *PassBase<T>::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<class T> inline PassBase<T> &PassBase<T>::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<class T> void PassBase<T>::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::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::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<class T> std::string PassBase<T>::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::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::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<class T> +inline void PassBase<T>::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<class T> inline void PassBase<T>::draw(GPUBatch *batch, ResourceHandle handle) +{ + this->draw(batch, -1, -1, -1, handle); +} + +template<class T> +inline void PassBase<T>::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<class T> +inline void PassBase<T>::draw_indirect(GPUBatch *batch, + StorageBuffer<DrawCommand, true> &indirect_buffer, + ResourceHandle handle) +{ + BLI_assert(shader_); + create_command(Type::DrawIndirect).draw_indirect = {batch, &indirect_buffer, handle}; +} + +template<class T> +inline void PassBase<T>::draw_procedural_indirect( + GPUPrimType primitive, + StorageBuffer<DrawCommand, true> &indirect_buffer, + ResourceHandle handle) +{ + this->draw_indirect(procedural_batch_get(primitive), indirect_buffer, handle); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Compute Dispatch Implementation + * \{ */ + +template<class T> inline void PassBase<T>::dispatch(int3 group_len) +{ + BLI_assert(shader_); + create_command(Type::Dispatch).dispatch = {group_len}; +} + +template<class T> inline void PassBase<T>::dispatch(int3 *group_len) +{ + BLI_assert(shader_); + create_command(Type::Dispatch).dispatch = {group_len}; +} + +template<class T> +inline void PassBase<T>::dispatch(StorageBuffer<DispatchCommand> &indirect_buffer) +{ + BLI_assert(shader_); + create_command(Type::DispatchIndirect).dispatch_indirect = {&indirect_buffer}; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Clear Implementation + * \{ */ + +template<class T> inline void PassBase<T>::clear_color(float4 color) +{ + this->clear(GPU_COLOR_BIT, color, 0.0f, 0); +} + +template<class T> inline void PassBase<T>::clear_depth(float depth) +{ + this->clear(GPU_DEPTH_BIT, float4(0.0f), depth, 0); +} + +template<class T> inline void PassBase<T>::clear_stencil(uint8_t stencil) +{ + this->clear(GPU_STENCIL_BIT, float4(0.0f), 0.0f, stencil); +} + +template<class T> inline void PassBase<T>::clear_depth_stencil(float depth, uint8_t stencil) +{ + this->clear(GPU_DEPTH_BIT | GPU_STENCIL_BIT, float4(0.0f), depth, stencil); +} + +template<class T> +inline void PassBase<T>::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<class T> inline void PassBase<T>::barrier(eGPUBarrier type) +{ + create_command(Type::Barrier).barrier = {type}; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name State Implementation + * \{ */ + +template<class T> inline void PassBase<T>::state_set(DRWState state) +{ + create_command(Type::StateSet).state_set = {state}; +} + +template<class T> +inline void PassBase<T>::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<class T> inline void PassBase<T>::shader_set(GPUShader *shader) +{ + shader_ = shader; + create_command(Type::ShaderBind).shader_bind = {shader}; +} + +template<class T> inline void PassBase<T>::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<GPUMaterialTexture>(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<class T> inline int PassBase<T>::push_constant_offset(const char *name) +{ + return GPU_shader_get_uniform(shader_, name); +} + +template<class T> inline void PassBase<T>::bind_ssbo(const char *name, GPUStorageBuf *buffer) +{ + this->bind_ssbo(GPU_shader_get_ssbo(shader_, name), buffer); +} + +template<class T> inline void PassBase<T>::bind_ubo(const char *name, GPUUniformBuf *buffer) +{ + this->bind_ubo(GPU_shader_get_uniform_block_binding(shader_, name), buffer); +} + +template<class T> +inline void PassBase<T>::bind_texture(const char *name, + GPUTexture *texture, + eGPUSamplerState state) +{ + this->bind_texture(GPU_shader_get_texture_binding(shader_, name), texture, state); +} + +template<class T> inline void PassBase<T>::bind_image(const char *name, GPUTexture *image) +{ + this->bind_image(GPU_shader_get_texture_binding(shader_, name), image); +} + +template<class T> inline void PassBase<T>::bind_ssbo(int slot, GPUStorageBuf *buffer) +{ + create_command(Type::ResourceBind).resource_bind = {slot, buffer}; +} + +template<class T> inline void PassBase<T>::bind_ubo(int slot, GPUUniformBuf *buffer) +{ + create_command(Type::ResourceBind).resource_bind = {slot, buffer}; +} + +template<class T> +inline void PassBase<T>::bind_texture(int slot, GPUTexture *texture, eGPUSamplerState state) +{ + create_command(Type::ResourceBind).resource_bind = {slot, texture, state}; +} + +template<class T> inline void PassBase<T>::bind_image(int slot, GPUTexture *image) +{ + create_command(Type::ResourceBind).resource_bind = {slot, as_image(image)}; +} + +template<class T> inline void PassBase<T>::bind_ssbo(const char *name, GPUStorageBuf **buffer) +{ + this->bind_ssbo(GPU_shader_get_ssbo(shader_, name), buffer); +} + +template<class T> inline void PassBase<T>::bind_ubo(const char *name, GPUUniformBuf **buffer) +{ + this->bind_ubo(GPU_shader_get_uniform_block_binding(shader_, name), buffer); +} + +template<class T> +inline void PassBase<T>::bind_texture(const char *name, + GPUTexture **texture, + eGPUSamplerState state) +{ + this->bind_texture(GPU_shader_get_texture_binding(shader_, name), texture, state); +} + +template<class T> inline void PassBase<T>::bind_image(const char *name, GPUTexture **image) +{ + this->bind_image(GPU_shader_get_texture_binding(shader_, name), image); +} + +template<class T> inline void PassBase<T>::bind_ssbo(int slot, GPUStorageBuf **buffer) +{ + + create_command(Type::ResourceBind).resource_bind = {slot, buffer}; +} + +template<class T> inline void PassBase<T>::bind_ubo(int slot, GPUUniformBuf **buffer) +{ + create_command(Type::ResourceBind).resource_bind = {slot, buffer}; +} + +template<class T> +inline void PassBase<T>::bind_texture(int slot, GPUTexture **texture, eGPUSamplerState state) +{ + create_command(Type::ResourceBind).resource_bind = {slot, texture, state}; +} + +template<class T> inline void PassBase<T>::bind_image(int slot, GPUTexture **image) +{ + create_command(Type::ResourceBind).resource_bind = {slot, as_image(image)}; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Push Constant Implementation + * \{ */ + +template<class T> inline void PassBase<T>::push_constant(const char *name, const float &data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> inline void PassBase<T>::push_constant(const char *name, const float2 &data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> inline void PassBase<T>::push_constant(const char *name, const float3 &data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> inline void PassBase<T>::push_constant(const char *name, const float4 &data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> inline void PassBase<T>::push_constant(const char *name, const int &data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> inline void PassBase<T>::push_constant(const char *name, const int2 &data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> inline void PassBase<T>::push_constant(const char *name, const int3 &data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> inline void PassBase<T>::push_constant(const char *name, const int4 &data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> inline void PassBase<T>::push_constant(const char *name, const bool &data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> +inline void PassBase<T>::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<class T> +inline void PassBase<T>::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<class T> +inline void PassBase<T>::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<class T> +inline void PassBase<T>::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<class T> +inline void PassBase<T>::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<class T> +inline void PassBase<T>::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<class T> +inline void PassBase<T>::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<class T> +inline void PassBase<T>::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<class T> inline void PassBase<T>::push_constant(const char *name, const float4x4 *data) +{ + create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; +} + +template<class T> inline void PassBase<T>::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<float4x4 *>(&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 |