diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2022-08-20 22:58:08 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2022-08-20 23:22:57 +0300 |
commit | dec817524fd5964cc5a92b38ef19b78d86833963 (patch) | |
tree | c9cccbe8e1952c95481b0fbae94fc40f549789ab | |
parent | dcc42042077c65663aa398a25dd800243dff2e27 (diff) |
Refactor Pass class
Now there is a still a base class but no virtual functions. The different
implementation of draw are declared as method shadowing the base class
method.
Sub pass is limited to 1 depth. Cannot have nested subpass.
Test is now passing
-rw-r--r-- | source/blender/draw/intern/draw_command.cc | 18 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_command.hh | 114 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_manager.cc | 7 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_manager.hh | 12 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_pass.cc | 120 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_pass.hh | 491 | ||||
-rw-r--r-- | source/blender/draw/tests/draw_pass_test.cc | 33 |
7 files changed, 493 insertions, 302 deletions
diff --git a/source/blender/draw/intern/draw_command.cc b/source/blender/draw/intern/draw_command.cc index ef47479875a..72f3911fda5 100644 --- a/source/blender/draw/intern/draw_command.cc +++ b/source/blender/draw/intern/draw_command.cc @@ -326,10 +326,13 @@ std::string PushConstant::serialize() const std::string Draw::serialize() const { - return std::string(".draw(inst_len=") + std::to_string(instance_len) + - ", vert_len=" + std::to_string(vertex_len) + - ", vert_first=" + std::to_string(vertex_first) + - ", res_id=" + std::to_string(handle.resource_index()) + ")"; + std::string inst_len = (instance_len == (uint)-1) ? "from_batch" : std::to_string(instance_len); + std::string vert_len = (vertex_len == (uint)-1) ? "from_batch" : std::to_string(vertex_len); + std::string vert_first = (vertex_first == (uint)-1) ? "from_batch" : + std::to_string(vertex_first); + return std::string(".draw(inst_len=") + inst_len + ", vert_len=" + vert_len + + ", vert_first=" + vert_first + ", res_id=" + std::to_string(handle.resource_index()) + + ")"; } std::string DrawIndirect::serialize() const @@ -371,7 +374,7 @@ std::string Clear::serialize() const } } if (eGPUFrameBufferBits(clear_channels) & GPU_STENCIL_BIT) { - ss << "stencil=" << std::bitset<8>(stencil) << ")"; + ss << "stencil=0b" << std::bitset<8>(stencil) << ")"; } return std::string(".clear(") + ss.str() + ")"; } @@ -385,9 +388,8 @@ std::string StateSet::serialize() const std::string StencilSet::serialize() const { std::stringstream ss; - ss << ".stencil_set(write_mask=" << std::bitset<8>(write_mask) - << ", compare_mask=" << std::bitset<8>(compare_mask) - << ", reference=" << std::bitset<8>(reference); + ss << ".stencil_set(write_mask=0b" << std::bitset<8>(write_mask) << ", compare_mask=0b" + << std::bitset<8>(compare_mask) << ", reference=0b" << std::bitset<8>(reference); return ss.str(); } diff --git a/source/blender/draw/intern/draw_command.hh b/source/blender/draw/intern/draw_command.hh index f977d271cc2..a2168201360 100644 --- a/source/blender/draw/intern/draw_command.hh +++ b/source/blender/draw/intern/draw_command.hh @@ -56,6 +56,7 @@ enum class Type : uint8_t { */ None = 0, + /** Commands stored as Undetermined in regular command buffer. */ Barrier, Clear, Dispatch, @@ -67,6 +68,10 @@ enum class Type : uint8_t { ShaderBind, StateSet, StencilSet, + + /** Special commands stored in separate buffers. */ + SubPass, + MultiDraw, }; /** @@ -75,12 +80,10 @@ enum class Type : uint8_t { * stream. */ struct Header { - /** Command index in command heap of this type. */ - uint command_index; /** Command type. */ Type type; - /** Special hack as we store a SubPass as 2 commands. */ - bool is_subpass; + /** Command index in command heap of this type. */ + uint command_index; }; struct ShaderBind { @@ -103,6 +106,8 @@ struct ResourceBind { } type; union { + /** TODO: Use draw::Texture|StorageBuffer|UniformBuffer as resources as they will give more + * debug info. */ GPUUniformBuf *uniform_buf; GPUUniformBuf **uniform_buf_ref; GPUStorageBuf *storage_buf; @@ -218,8 +223,8 @@ struct PushConstant { struct Draw { GPUBatch *batch; uint instance_len; - uint vertex_first; uint vertex_len; + uint vertex_first; ResourceHandle handle; void execute(RecordingState &state) const; @@ -291,18 +296,93 @@ struct StencilSet { std::string serialize() const; }; -union Undetermined { - ShaderBind shader_bind; - ResourceBind resource_bind; - PushConstant push_constant; - Draw draw; - DrawIndirect draw_indirect; - Dispatch dispatch; - DispatchIndirect dispatch_indirect; - Barrier barrier; - Clear clear; - StateSet state_set; - StencilSet stencil_set; +struct Undetermined { + union { + ShaderBind shader_bind; + ResourceBind resource_bind; + PushConstant push_constant; + Draw draw; + DrawIndirect draw_indirect; + Dispatch dispatch; + DispatchIndirect dispatch_indirect; + Barrier barrier; + Clear clear; + StateSet state_set; + StencilSet stencil_set; + }; + + void execute(const command::Type &type, command::RecordingState &state) const + { + switch (type) { + case command::Type::ShaderBind: + shader_bind.execute(state); + break; + case command::Type::ResourceBind: + resource_bind.execute(); + break; + case command::Type::PushConstant: + push_constant.execute(state); + break; + case command::Type::Draw: + draw.execute(state); + break; + case command::Type::DrawIndirect: + draw_indirect.execute(state); + break; + case command::Type::Dispatch: + dispatch.execute(state); + break; + case command::Type::DispatchIndirect: + dispatch_indirect.execute(state); + break; + case command::Type::Barrier: + barrier.execute(); + break; + case command::Type::Clear: + clear.execute(); + break; + case command::Type::StateSet: + state_set.execute(state); + break; + case command::Type::StencilSet: + stencil_set.execute(); + break; + default: + /* Skip Type::None. */ + break; + } + } + + std::string serialize(const command::Type &type) const + { + switch (type) { + case command::Type::ShaderBind: + return shader_bind.serialize(); + case command::Type::ResourceBind: + return resource_bind.serialize(); + case command::Type::PushConstant: + return push_constant.serialize(); + case command::Type::Draw: + return draw.serialize(); + case command::Type::DrawIndirect: + return draw_indirect.serialize(); + case command::Type::Dispatch: + return dispatch.serialize(); + case command::Type::DispatchIndirect: + return dispatch_indirect.serialize(); + case command::Type::Barrier: + return barrier.serialize(); + case command::Type::Clear: + return clear.serialize(); + case command::Type::StateSet: + return state_set.serialize(); + case command::Type::StencilSet: + return stencil_set.serialize(); + default: + /* Skip Type::None. */ + return ""; + } + } }; /** Try to keep the command size as low as possible for performance. */ diff --git a/source/blender/draw/intern/draw_manager.cc b/source/blender/draw/intern/draw_manager.cc index 8fee875b8cf..5995c548228 100644 --- a/source/blender/draw/intern/draw_manager.cc +++ b/source/blender/draw/intern/draw_manager.cc @@ -9,15 +9,16 @@ namespace blender::draw { -void Manager::submit(const Pass &pass) +void Manager::submit(const PassSimple &pass) { command::RecordingState state; pass.submit(state); } -std::string Manager::serialize(const Pass &pass) +void Manager::submit(const PassMain &pass) { - return pass.serialize(); + command::RecordingState state; + pass.submit(state); } } // namespace blender::draw diff --git a/source/blender/draw/intern/draw_manager.hh b/source/blender/draw/intern/draw_manager.hh index f559e810645..f2db36d522e 100644 --- a/source/blender/draw/intern/draw_manager.hh +++ b/source/blender/draw/intern/draw_manager.hh @@ -23,16 +23,8 @@ class Manager { * Submit a pass for drawing. All resource reference will be dereferenced and commands will be * sent to GPU. */ - /* TODO(fclem): Why doesn't this work? */ - // void submit(const PassCommon &pass); - void submit(const Pass &pass); - - /** - * Will turn the command stream into a debug string. - */ - /* TODO(fclem): Why doesn't this work? */ - // std::string serialize(const PassCommon &pass); - std::string serialize(const Pass &pass); + void submit(const PassSimple &pass); + void submit(const PassMain &pass); }; } // namespace blender::draw diff --git a/source/blender/draw/intern/draw_pass.cc b/source/blender/draw/intern/draw_pass.cc index f767613c3f2..d0d397c8295 100644 --- a/source/blender/draw/intern/draw_pass.cc +++ b/source/blender/draw/intern/draw_pass.cc @@ -10,15 +10,131 @@ namespace blender::draw { /* -------------------------------------------------------------------- */ -/** \name Pass +/** \name Pass Submission * \{ */ +void PassSimple::submit(command::RecordingState &state) const +{ + for (const command::Header &header : headers_) { + switch (header.type) { + case Type::SubPass: + sub_passes_[header.command_index].submit(state); + break; + default: + commands_[header.command_index].execute(header.type, state); + break; + } + } +} + +void PassSimple::Sub::submit(command::RecordingState &state) const +{ + for (const command::Header &header : headers_) { + commands_[header.command_index].execute(header.type, state); + } +} + +void PassMain::submit(command::RecordingState &state) const +{ + for (const command::Header &header : headers_) { + switch (header.type) { + case Type::SubPass: + sub_passes_[header.command_index].submit(state); + break; + case Type::MultiDraw: + /* TODO */ + break; + default: + commands_[header.command_index].execute(header.type, state); + break; + } + } +} + +void PassMain::Sub::submit(command::RecordingState &state) const +{ + for (const command::Header &header : headers_) { + switch (header.type) { + case Type::MultiDraw: + /* TODO */ + break; + default: + commands_[header.command_index].execute(header.type, state); + break; + } + } +} + /** \} */ /* -------------------------------------------------------------------- */ -/** \name Sub Pass +/** \name Pass Serialization * \{ */ +std::string PassSimple::serialize() const +{ + std::stringstream ss; + ss << "PassSimple(" << debug_name << ")" << std::endl; + for (const command::Header &header : headers_) { + switch (header.type) { + case Type::SubPass: + ss << sub_passes_[header.command_index].serialize() << std::endl; + break; + default: + ss << commands_[header.command_index].serialize(header.type) << std::endl; + break; + } + } + return ss.str(); +} + +std::string PassSimple::Sub::serialize() const +{ + std::stringstream ss; + ss << ".sub(" << debug_name << ")" << std::endl; + for (const command::Header &header : headers_) { + ss << " " << commands_[header.command_index].serialize(header.type) << std::endl; + } + return ss.str(); +} + +std::string PassMain::serialize() const +{ + std::stringstream ss; + ss << "PassMain(" << debug_name << ")" << std::endl; + for (const command::Header &header : headers_) { + switch (header.type) { + case Type::SubPass: + ss << sub_passes_[header.command_index].serialize() << std::endl; + break; + case Type::MultiDraw: + /* TODO */ + break; + default: + ss << commands_[header.command_index].serialize(header.type) << std::endl; + break; + } + } + return ss.str(); +} + +std::string PassMain::Sub::serialize() const +{ + std::stringstream ss; + ss << ".sub(" << debug_name << ")" << std::endl; + for (const command::Header &header : headers_) { + switch (header.type) { + case Type::MultiDraw: + /* TODO */ + break; + default: + ss << " " << commands_[header.command_index].serialize(header.type) << std::endl; + break; + } + } + return ss.str(); +} + /** \} */ } // namespace blender::draw diff --git a/source/blender/draw/intern/draw_pass.hh b/source/blender/draw/intern/draw_pass.hh index f4721ba07f8..4d4f74e1425 100644 --- a/source/blender/draw/intern/draw_pass.hh +++ b/source/blender/draw/intern/draw_pass.hh @@ -10,18 +10,20 @@ * only change in resource load (memory & CPU usage). They can be swapped without any functional * change. * - * `Pass`: + * `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. * - * `PassSub`: + * `Pass(Main|Simple)::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 durring submission. * + * `PassSimple`: + * Does not have the overhead of #PassMain but does not have the culling and batching optimization. * * 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 @@ -49,27 +51,27 @@ class Manager; /** \name Pass API * \{ */ +namespace detail { + /** * Public API of a draw pass. */ -class Pass { +class PassBase { friend Manager; - private: + protected: /** Highest level of the command stream. Split command stream in different command types. */ Vector<command::Header> headers_; /** Commands referenced by headers (which contains their types). */ Vector<command::Undetermined> commands_; - /** Sub-passes referenced by headers. */ - // Vector<Pass> sub_passes_; /** Currently bound shader. Used for interface queries. */ GPUShader *shader_; public: const char *debug_name; - Pass() = delete; - Pass(const char *name = "Unamed") : debug_name(name){}; + PassBase() = delete; + PassBase(const char *name = "Unamed") : debug_name(name){}; /** * Reset the pass command pool. @@ -77,11 +79,6 @@ class Pass { void init(); /** - * Create a sub-pass inside this pass. The sub-pass memory is auto managed. - */ - // Sub &sub(); - - /** * Changes the fixed function pipeline state. * Starts as DRW_STATE_NO_DRAW at the start of a Pass submission. * SubPass inherit previous pass state. @@ -123,25 +120,28 @@ class Pass { uint vertex_len = -1, uint vertex_first = -1, ResourceHandle handle = {0}); - void draw(GPUBatch *batch, - StorageBuffer<DrawCommand> &indirect_buffer, - ResourceHandle handle = {0}); + void draw_indirect(GPUBatch *batch, + StorageBuffer<DrawCommand> &indirect_buffer, + ResourceHandle handle = {0}); /** Shorter version for the common case. */ - void draw(GPUBatch *batch, ResourceHandle handle); + void draw(GPUBatch *batch, ResourceHandle handle) + { + draw(batch, -1, -1, -1, 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(GPUPrimType primitive, - uint instance_len, - uint vertex_len, - uint vertex_first = -1, - ResourceHandle handle = {0}); - void draw(GPUPrimType primitive, - StorageBuffer<DrawCommand> &indirect_buffer, - ResourceHandle handle = {0}); + void draw_procedural(GPUPrimType primitive, + uint instance_len, + uint vertex_len, + uint vertex_first = -1, + ResourceHandle handle = {0}); + void draw_procedural_indirect(GPUPrimType primitive, + StorageBuffer<DrawCommand> &indirect_buffer, + ResourceHandle handle = {0}); /** * Record a compute dispatch call. @@ -215,14 +215,14 @@ class Pass { /** * Turn the pass into a string for inspection. */ - std::string serialize() const; + virtual std::string serialize() const = 0; - friend std::ostream &operator<<(std::ostream &stream, const Pass &pass) + friend std::ostream &operator<<(std::ostream &stream, const PassBase &pass) { return stream << pass.serialize(); } - private: + protected: /** * Internal Helpers */ @@ -233,21 +233,67 @@ class Pass { GPUBatch *procedural_batch_get(GPUPrimType primitive); - static void submit_command(command::RecordingState &state, - const command::Type &type, - const command::Undetermined &command); - - static std::string serialize_command(const command::Type &type, - const command::Undetermined &command); - /** * Return a new command recorded with the given type. */ command::Undetermined &create_command(command::Type type); +}; + +} // namespace detail + +/** + * Normal pass type. No visibility or draw-call optimisation. + */ +class PassSimple : public detail::PassBase { + friend Manager; + + public: + class Sub : public detail::PassBase { + friend PassSimple; + + public: + /** + * Turn the pass into a string for inspection. + */ + std::string serialize() const; + + private: + Sub(const char *name) : PassBase(name){}; + + void submit(command::RecordingState &state) const; + }; + + private: + /** Sub-passes referenced by headers. */ + Vector<PassSimple::Sub> sub_passes_; + + public: + PassSimple() = delete; + PassSimple(const char *name) : PassBase(name){}; + + /** + * Create a sub-pass inside this pass. The sub-pass memory is auto managed. + */ + PassSimple::Sub &sub(const char *name); + + 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. */ + void draw(GPUBatch *batch, ResourceHandle handle) + { + draw(batch, -1, -1, -1, handle); + } /** - * Submit a Pass as GPU commands. Should only be called by draw::Manager. + * Turn the pass into a string for inspection. */ + std::string serialize() const; + + private: void submit(command::RecordingState &state) const; }; @@ -258,115 +304,129 @@ class 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. */ -class PassMain : public Pass { +class PassMain : public detail::PassBase { + friend Manager; + + public: + class Sub : public detail::PassBase { + friend PassMain; + + public: + /** + * Turn the pass into a string for inspection. + */ + std::string serialize() const; + + private: + command::MultiDrawBuffer &multi_draws_; + + Sub(const char *name, command::MultiDrawBuffer &multi_draws) + : PassBase(name), multi_draws_(multi_draws){}; + + void draw(GPUBatch *batch, + uint instance_len = -1, + uint vertex_len = -1, + uint vertex_first = -1, + ResourceHandle handle = {0}); + + void submit(command::RecordingState &state) const; + }; + private: + /** Sub-passes referenced by headers. */ + Vector<PassMain::Sub> sub_passes_; /** Multi draw indirect rendering for many draw calls efficient rendering. */ command::MultiDrawBuffer multi_draws_; public: PassMain() = delete; - PassMain(const char *name) : Pass(name){}; + PassMain(const char *name) : PassBase(name){}; + + /** + * Create a sub-pass inside this pass. The sub-pass memory is auto managed. + */ + PassMain::Sub &sub(const char *name); -#if 0 void draw(GPUBatch *batch, uint instance_len = -1, uint vertex_len = -1, uint vertex_first = -1, ResourceHandle handle = {0}); - void draw(GPUBatch *batch, - StorageBuffer<DrawCommand> &indirect_buffer, - ResourceHandle handle = {0}); -#endif + + /** Shorter version for the common case. */ + void draw(GPUBatch *batch, ResourceHandle handle) + { + draw(batch, -1, -1, -1, handle); + } + + /** + * Turn the pass into a string for inspection. + */ + std::string serialize() const; + + private: + void submit(command::RecordingState &state) const; }; /** \} */ /* -------------------------------------------------------------------- */ -/** \name Pass +/** \name PassSimple Implementation * \{ */ -/** - * Reset the pass command pool. - */ -inline void Pass::init() +inline PassSimple::Sub &PassSimple::sub(const char *name) +{ + int64_t index = sub_passes_.append_and_get_index(PassSimple::Sub(name)); + headers_.append({command::Type::SubPass, static_cast<uint>(index)}); + return sub_passes_[index]; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name PassMain Implementation + * \{ */ + +inline PassMain::Sub &PassMain::sub(const char *name) +{ + int64_t index = sub_passes_.append_and_get_index(PassMain::Sub(name, multi_draws_)); + headers_.append({command::Type::SubPass, static_cast<uint>(index)}); + return sub_passes_[index]; +} + +/** \} */ + +namespace detail { + +/* -------------------------------------------------------------------- */ +/** \name PassBase Implementation + * \{ */ + +inline void PassBase::init() { headers_.clear(); commands_.clear(); } -inline command::Undetermined &Pass::create_command(command::Type type) +inline command::Undetermined &PassBase::create_command(command::Type type) { int64_t index = commands_.append_and_get_index({}); - headers_.append({static_cast<uint>(index), type, false}); + headers_.append({type, static_cast<uint>(index)}); return commands_[index]; } -inline void Pass::submit(command::RecordingState &state) const -{ -#if 0 - if (command_last_ != -1) { - uint cmd_index = 0; - while (cmd_index != -1) { - command::Header &header = headers_[cmd_index]; - cmd_index = header.next; - - if (header.is_subpass) { - /** WARNING: Recursive. */ - PassSub &sub = *reinterpret_cast<PassSub *>(&commands_[header.command_index]); - ss << submit(); - } - else if (false) { - /** TODO(fclem): MultiDraw */ - } - else { - ss << detail::PassCommon::submit_command( - state, header.type, commands_[header.command_index]) - << std::endl; - } - } - } -#endif -} - -inline std::string Pass::serialize() const -{ -#if 0 - if (command_last_ != -1) { - uint cmd_index = 0; - while (cmd_index != -1) { - command::Header &header = headers_[cmd_index]; - cmd_index = header.next; - - if (header.is_subpass) { - /** WARNING: Recursive. */ - PassSub &sub = *reinterpret_cast<PassSub *>(&commands_[header.command_index]); - ss << sub.serialize(); - } - else if (false) { - /** TODO(fclem): MultiDraw */ - } - else { - ss << detail::PassCommon::serialize_command(header.type, commands_[header.command_index]) - << std::endl; - } - } - } -#endif - return ""; -} - /** \} */ /* -------------------------------------------------------------------- */ -/** \name Draw Calls +/** \name Draw Calls Implementation * \{ */ -inline void Pass::clear(eGPUFrameBufferBits planes, float4 color, float depth, uint8_t stencil) +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}; } -inline GPUBatch *Pass::procedural_batch_get(GPUPrimType primitive) +inline GPUBatch *PassBase::procedural_batch_get(GPUPrimType primitive) { switch (primitive) { case GPU_PRIM_POINTS: @@ -384,7 +444,7 @@ inline GPUBatch *Pass::procedural_batch_get(GPUPrimType primitive) } } -inline void Pass::draw( +inline void PassBase::draw( GPUBatch *batch, uint instance_len, uint vertex_len, uint vertex_first, ResourceHandle handle) { if (instance_len == 0 || vertex_len == 0) { @@ -394,54 +454,49 @@ inline void Pass::draw( create_command(Type::Draw).draw = {batch, instance_len, vertex_len, vertex_first, handle}; } -inline void Pass::draw(GPUBatch *batch, - StorageBuffer<DrawCommand> &indirect_buffer, - ResourceHandle handle) +inline void PassBase::draw_indirect(GPUBatch *batch, + StorageBuffer<DrawCommand> &indirect_buffer, + ResourceHandle handle) { BLI_assert(shader_); create_command(Type::DrawIndirect).draw_indirect = {batch, &indirect_buffer, handle}; } -inline void Pass::draw(GPUPrimType primitive, - uint instance_len, - uint vertex_len, - uint vertex_first, - ResourceHandle handle) +inline void PassBase::draw_procedural(GPUPrimType primitive, + uint instance_len, + uint vertex_len, + uint vertex_first, + ResourceHandle handle) { draw(procedural_batch_get(primitive), instance_len, vertex_len, vertex_first, handle); } -inline void Pass::draw(GPUPrimType primitive, - StorageBuffer<DrawCommand> &indirect_buffer, - ResourceHandle handle) +inline void PassBase::draw_procedural_indirect(GPUPrimType primitive, + StorageBuffer<DrawCommand> &indirect_buffer, + ResourceHandle handle) { - draw(procedural_batch_get(primitive), indirect_buffer, handle); -} - -inline void Pass::draw(GPUBatch *batch, ResourceHandle handle) -{ - draw(batch, -1, -1, -1, handle); + draw_indirect(procedural_batch_get(primitive), indirect_buffer, handle); } /** \} */ /* -------------------------------------------------------------------- */ -/** \name Compute Dispatch +/** \name Compute Dispatch Implementation * \{ */ -inline void Pass::dispatch(int3 group_len) +inline void PassBase::dispatch(int3 group_len) { BLI_assert(shader_); create_command(Type::Dispatch).dispatch = {group_len}; } -inline void Pass::dispatch(int3 *group_len) +inline void PassBase::dispatch(int3 *group_len) { BLI_assert(shader_); create_command(Type::Dispatch).dispatch = {group_len}; } -inline void Pass::dispatch(StorageBuffer<DispatchCommand> &indirect_buffer) +inline void PassBase::dispatch(StorageBuffer<DispatchCommand> &indirect_buffer) { BLI_assert(shader_); create_command(Type::DispatchIndirect).dispatch_indirect = {&indirect_buffer}; @@ -450,30 +505,30 @@ inline void Pass::dispatch(StorageBuffer<DispatchCommand> &indirect_buffer) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Clear +/** \name Clear Implementation * \{ */ -inline void Pass::clear_color(float4 color) +inline void PassBase::clear_color(float4 color) { clear(GPU_COLOR_BIT, color, 0.0f, 0); } -inline void Pass::clear_depth(float depth) +inline void PassBase::clear_depth(float depth) { clear(GPU_DEPTH_BIT, float4(0.0f), depth, 0); } -inline void Pass::clear_stencil(uint8_t stencil) +inline void PassBase::clear_stencil(uint8_t stencil) { clear(GPU_STENCIL_BIT, float4(0.0f), 0.0f, stencil); } -inline void Pass::clear_depth_stencil(float depth, uint8_t stencil) +inline void PassBase::clear_depth_stencil(float depth, uint8_t stencil) { clear(GPU_DEPTH_BIT | GPU_STENCIL_BIT, float4(0.0f), depth, stencil); } -inline void Pass::clear_color_depth_stencil(float4 color, float depth, uint8_t stencil) +inline void PassBase::clear_color_depth_stencil(float4 color, float depth, uint8_t stencil) { clear(GPU_DEPTH_BIT | GPU_STENCIL_BIT | GPU_COLOR_BIT, color, depth, stencil); } @@ -481,10 +536,10 @@ inline void Pass::clear_color_depth_stencil(float4 color, float depth, uint8_t s /** \} */ /* -------------------------------------------------------------------- */ -/** \name Barrier +/** \name Barrier Implementation * \{ */ -inline void Pass::barrier(eGPUBarrier type) +inline void PassBase::barrier(eGPUBarrier type) { create_command(Type::Barrier).barrier = {type}; } @@ -492,20 +547,20 @@ inline void Pass::barrier(eGPUBarrier type) /** \} */ /* -------------------------------------------------------------------- */ -/** \name State +/** \name State Implementation * \{ */ -inline void Pass::state_set(DRWState state) +inline void PassBase::state_set(DRWState state) { create_command(Type::StateSet).state_set = {state}; } -inline void Pass::state_stencil(uint8_t write_mask, uint8_t reference, uint8_t compare_mask) +inline void PassBase::state_stencil(uint8_t write_mask, uint8_t reference, uint8_t compare_mask) { create_command(Type::StencilSet).stencil_set = {write_mask, reference, compare_mask}; } -inline void Pass::shader_set(GPUShader *shader) +inline void PassBase::shader_set(GPUShader *shader) { shader_ = shader; create_command(Type::ShaderBind).shader_bind = {shader}; @@ -514,91 +569,91 @@ inline void Pass::shader_set(GPUShader *shader) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Resource bind +/** \name Resource bind Implementation * \{ */ -inline int Pass::push_constant_offset(const char *name) +inline int PassBase::push_constant_offset(const char *name) { return GPU_shader_get_uniform(shader_, name); } -inline void Pass::bind(const char *name, GPUStorageBuf *buffer) +inline void PassBase::bind(const char *name, GPUStorageBuf *buffer) { bind(GPU_shader_get_uniform_block_binding(shader_, name), buffer); } -inline void Pass::bind(const char *name, GPUUniformBuf *buffer) +inline void PassBase::bind(const char *name, GPUUniformBuf *buffer) { bind(GPU_shader_get_ssbo(shader_, name), buffer); } -inline void Pass::bind(const char *name, GPUTexture *texture, eGPUSamplerState state) +inline void PassBase::bind(const char *name, GPUTexture *texture, eGPUSamplerState state) { bind(GPU_shader_get_texture_binding(shader_, name), texture, state); } -inline void Pass::bind(const char *name, draw::Image *image) +inline void PassBase::bind(const char *name, draw::Image *image) { bind(GPU_shader_get_texture_binding(shader_, name), image); } -inline void Pass::bind(int slot, GPUStorageBuf *buffer) +inline void PassBase::bind(int slot, GPUStorageBuf *buffer) { create_command(Type::ResourceBind).resource_bind = {slot, buffer}; } -inline void Pass::bind(int slot, GPUUniformBuf *buffer) +inline void PassBase::bind(int slot, GPUUniformBuf *buffer) { create_command(Type::ResourceBind).resource_bind = {slot, buffer}; } -inline void Pass::bind(int slot, GPUTexture *texture, eGPUSamplerState state) +inline void PassBase::bind(int slot, GPUTexture *texture, eGPUSamplerState state) { create_command(Type::ResourceBind).resource_bind = {slot, texture, state}; } -inline void Pass::bind(int slot, draw::Image *image) +inline void PassBase::bind(int slot, draw::Image *image) { create_command(Type::ResourceBind).resource_bind = {slot, image}; } -inline void Pass::bind(const char *name, GPUStorageBuf **buffer) +inline void PassBase::bind(const char *name, GPUStorageBuf **buffer) { bind(GPU_shader_get_uniform_block_binding(shader_, name), buffer); } -inline void Pass::bind(const char *name, GPUUniformBuf **buffer) +inline void PassBase::bind(const char *name, GPUUniformBuf **buffer) { bind(GPU_shader_get_ssbo(shader_, name), buffer); } -inline void Pass::bind(const char *name, GPUTexture **texture, eGPUSamplerState state) +inline void PassBase::bind(const char *name, GPUTexture **texture, eGPUSamplerState state) { bind(GPU_shader_get_texture_binding(shader_, name), texture, state); } -inline void Pass::bind(const char *name, draw::Image **image) +inline void PassBase::bind(const char *name, draw::Image **image) { bind(GPU_shader_get_texture_binding(shader_, name), image); } -inline void Pass::bind(int slot, GPUStorageBuf **buffer) +inline void PassBase::bind(int slot, GPUStorageBuf **buffer) { create_command(Type::ResourceBind).resource_bind = {slot, buffer}; } -inline void Pass::bind(int slot, GPUUniformBuf **buffer) +inline void PassBase::bind(int slot, GPUUniformBuf **buffer) { create_command(Type::ResourceBind).resource_bind = {slot, buffer}; } -inline void Pass::bind(int slot, GPUTexture **texture, eGPUSamplerState state) +inline void PassBase::bind(int slot, GPUTexture **texture, eGPUSamplerState state) { create_command(Type::ResourceBind).resource_bind = {slot, texture, state}; } -inline void Pass::bind(int slot, draw::Image **image) +inline void PassBase::bind(int slot, draw::Image **image) { create_command(Type::ResourceBind).resource_bind = {slot, image}; } @@ -606,100 +661,100 @@ inline void Pass::bind(int slot, draw::Image **image) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Push Constant +/** \name Push Constant Implementation * \{ */ -inline void Pass::push_constant(const char *name, const float &data) +inline void PassBase::push_constant(const char *name, const float &data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } -inline void Pass::push_constant(const char *name, const float2 &data) +inline void PassBase::push_constant(const char *name, const float2 &data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } -inline void Pass::push_constant(const char *name, const float3 &data) +inline void PassBase::push_constant(const char *name, const float3 &data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } -inline void Pass::push_constant(const char *name, const float4 &data) +inline void PassBase::push_constant(const char *name, const float4 &data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } -inline void Pass::push_constant(const char *name, const int &data) +inline void PassBase::push_constant(const char *name, const int &data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } -inline void Pass::push_constant(const char *name, const int2 &data) +inline void PassBase::push_constant(const char *name, const int2 &data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } -inline void Pass::push_constant(const char *name, const int3 &data) +inline void PassBase::push_constant(const char *name, const int3 &data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } -inline void Pass::push_constant(const char *name, const int4 &data) +inline void PassBase::push_constant(const char *name, const int4 &data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } -inline void Pass::push_constant(const char *name, const bool &data) +inline void PassBase::push_constant(const char *name, const bool &data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } -inline void Pass::push_constant(const char *name, const float *data, int array_len) +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}; } -inline void Pass::push_constant(const char *name, const float2 *data, int array_len) +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}; } -inline void Pass::push_constant(const char *name, const float3 *data, int array_len) +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}; } -inline void Pass::push_constant(const char *name, const float4 *data, int array_len) +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}; } -inline void Pass::push_constant(const char *name, const int *data, int array_len) +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}; } -inline void Pass::push_constant(const char *name, const int2 *data, int array_len) +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}; } -inline void Pass::push_constant(const char *name, const int3 *data, int array_len) +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}; } -inline void Pass::push_constant(const char *name, const int4 *data, int array_len) +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}; } -inline void Pass::push_constant(const char *name, const float4x4 *data) +inline void PassBase::push_constant(const char *name, const float4x4 *data) { create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data}; } -inline void Pass::push_constant(const char *name, const float4x4 &data) +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. */ @@ -718,82 +773,8 @@ inline void Pass::push_constant(const char *name, const float4x4 &data) create_command(Type::None) = commands[2]; } -inline void Pass::submit_command(command::RecordingState &state, - const command::Type &type, - const command::Undetermined &command) -{ - switch (type) { - case command::Type::ShaderBind: - command.shader_bind.execute(state); - break; - case command::Type::ResourceBind: - command.resource_bind.execute(); - break; - case command::Type::PushConstant: - command.push_constant.execute(state); - break; - case command::Type::Draw: - command.draw.execute(state); - break; - case command::Type::DrawIndirect: - command.draw_indirect.execute(state); - break; - case command::Type::Dispatch: - command.dispatch.execute(state); - break; - case command::Type::DispatchIndirect: - command.dispatch_indirect.execute(state); - break; - case command::Type::Barrier: - command.barrier.execute(); - break; - case command::Type::Clear: - command.clear.execute(); - break; - case command::Type::StateSet: - command.state_set.execute(state); - break; - case command::Type::StencilSet: - command.stencil_set.execute(); - break; - default: - /* Skip Type::None. */ - break; - } -} - -inline std::string Pass::serialize_command(const command::Type &type, - const command::Undetermined &command) -{ - switch (type) { - case command::Type::ShaderBind: - return command.shader_bind.serialize(); - case command::Type::ResourceBind: - return command.resource_bind.serialize(); - case command::Type::PushConstant: - return command.push_constant.serialize(); - case command::Type::Draw: - return command.draw.serialize(); - case command::Type::DrawIndirect: - return command.draw_indirect.serialize(); - case command::Type::Dispatch: - return command.dispatch.serialize(); - case command::Type::DispatchIndirect: - return command.dispatch_indirect.serialize(); - case command::Type::Barrier: - return command.barrier.serialize(); - case command::Type::Clear: - return command.clear.serialize(); - case command::Type::StateSet: - return command.state_set.serialize(); - case command::Type::StencilSet: - return command.stencil_set.serialize(); - default: - /* Skip Type::None. */ - return ""; - } -} - /** \} */ +} // namespace detail + } // namespace blender::draw diff --git a/source/blender/draw/tests/draw_pass_test.cc b/source/blender/draw/tests/draw_pass_test.cc index c38c596b41d..8cd647e5894 100644 --- a/source/blender/draw/tests/draw_pass_test.cc +++ b/source/blender/draw/tests/draw_pass_test.cc @@ -10,8 +10,6 @@ namespace blender::draw { static void test_draw_pass_all_commands() { - Manager drw; - Texture tex; tex.ensure_2d(GPU_RGBA16, int2(1)); @@ -24,7 +22,7 @@ static void test_draw_pass_all_commands() float alpha = 0.0f; int3 dispatch_size(1); - Pass pass = {"test.all_commands"}; + PassSimple pass = {"test.all_commands"}; pass.init(); pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_STENCIL); pass.clear_color_depth_stencil(float4(0.25f, 0.5f, 100.0f, -2000.0f), 0.5f, 0xF0); @@ -40,7 +38,7 @@ static void test_draw_pass_all_commands() pass.bind("missing_ssbo", &ssbo); /* Should not crash. */ pass.push_constant("alpha", alpha); pass.push_constant("alpha", &alpha); - pass.draw(GPU_PRIM_TRIS, 1, 3); + pass.draw_procedural(GPU_PRIM_TRIS, 1, 3); /* Should not crash even if shader is not a compute. This is because we only serialize. */ /* TODO(fclem): Use real compute shader. */ @@ -53,10 +51,31 @@ static void test_draw_pass_all_commands() alpha = 1.0f; dispatch_size = int3(2); - std::string result = drw.serialize(pass); - StringRefNull expected = ""; + std::string result = pass.serialize(); + std::stringstream expected; + expected << "PassSimple(test.all_commands)" << std::endl; + expected << ".state_set(6)" << std::endl; + expected << ".clear(color=(0.25, 0.5, 100, -2000), depth=0.5, stencil=0b11110000))" << std::endl; + expected << ".stencil_set(write_mask=0b10000000, compare_mask=0b00001111, reference=0b10001111" + << std::endl; + expected << ".shader_bind(gpu_shader_3D_image_modulate_alpha)" << std::endl; + expected << ".bind_texture(0)" << std::endl; + expected << ".bind_texture_ref(0)" << std::endl; + expected << ".bind_image(-1)" << std::endl; + expected << ".bind_image_ref(-1)" << std::endl; + expected << ".bind_uniform_buf(-1)" << std::endl; + expected << ".bind_uniform_buf_ref(-1)" << std::endl; + expected << ".bind_storage_buf(-1)" << std::endl; + expected << ".bind_storage_buf_ref(-1)" << std::endl; + expected << ".push_constant(2, data=0)" << std::endl; + expected << ".push_constant(2, data=1)" << std::endl; + expected << ".draw(inst_len=1, vert_len=3, vert_first=from_batch, res_id=0)" << std::endl; + expected << ".shader_bind(gpu_shader_3D_image_modulate_alpha)" << std::endl; + expected << ".dispatch(1, 1, 1)" << std::endl; + expected << ".dispatch_ref(2, 2, 2)" << std::endl; + expected << ".barrier(4)" << std::endl; - EXPECT_EQ(result, expected); + EXPECT_EQ(result, expected.str()); } DRAW_TEST(draw_pass_all_commands) |