add GL/Buffer
This commit is contained in:
		| @@ -67,6 +67,7 @@ target_sources(nf7 | ||||
|     common/audio_queue.hh | ||||
|     common/dir.hh | ||||
|     common/dir_item.hh | ||||
|     common/factory.hh | ||||
|     common/file_base.hh | ||||
|     common/file_holder.hh | ||||
|     common/file_holder.cc | ||||
| @@ -76,6 +77,8 @@ target_sources(nf7 | ||||
|     common/generic_memento.hh | ||||
|     common/generic_type_info.hh | ||||
|     common/generic_watcher.hh | ||||
|     common/gl_fence.hh | ||||
|     common/gl_obj.hh | ||||
|     common/gui_config.hh | ||||
|     common/gui_context.hh | ||||
|     common/gui_dnd.hh | ||||
| @@ -133,6 +136,7 @@ target_sources(nf7 | ||||
|  | ||||
|     file/audio_context.cc | ||||
|     file/audio_device.cc | ||||
|     file/gl_obj.cc | ||||
|     file/luajit_context.cc | ||||
|     file/luajit_node.cc | ||||
|     file/node_imm.cc | ||||
|   | ||||
							
								
								
									
										27
									
								
								common/factory.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								common/factory.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "nf7.hh" | ||||
|  | ||||
| #include "common/future.hh" | ||||
|  | ||||
|  | ||||
| namespace nf7 { | ||||
|  | ||||
| template <typename T> | ||||
| class Factory : public nf7::File::Interface { | ||||
|  public: | ||||
|   using Product = T; | ||||
|  | ||||
|   Factory() = default; | ||||
|   Factory(const Factory&) = delete; | ||||
|   Factory(Factory&&) = delete; | ||||
|   Factory& operator=(const Factory&) = delete; | ||||
|   Factory& operator=(Factory&&) = delete; | ||||
|  | ||||
|   virtual Product Create() noexcept = 0; | ||||
| }; | ||||
|  | ||||
| template <typename T> | ||||
| using AsyncFactory = Factory<nf7::Future<T>>; | ||||
|  | ||||
| }  // namespace nf7 | ||||
| @@ -39,6 +39,7 @@ class Future final { | ||||
|   class Promise; | ||||
|   class Coro; | ||||
|  | ||||
|   using Product    = T; | ||||
|   using ThisFuture = nf7::Future<T>; | ||||
|   using Handle     = std::coroutine_handle<Promise>; | ||||
|   using Imm        = std::variant<T, std::exception_ptr>; | ||||
|   | ||||
							
								
								
									
										46
									
								
								common/gl_fence.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								common/gl_fence.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <chrono> | ||||
| #include <memory> | ||||
|  | ||||
| #include <GL/glew.h> | ||||
|  | ||||
| #include "nf7.hh" | ||||
|  | ||||
| #include "common/future.hh" | ||||
|  | ||||
|  | ||||
| namespace nf7::gl { | ||||
|  | ||||
| inline void Await(const std::shared_ptr<nf7::Context>&  ctx, | ||||
|                   nf7::Future<std::monostate>::Promise& pro, | ||||
|                   GLsync                                sync) noexcept { | ||||
|   GLsizei len; | ||||
|   GLint   v; | ||||
|   glGetSynciv(sync, GL_SYNC_STATUS, sizeof(v), &len, &v); | ||||
|   assert(0 == glGetError()); | ||||
|  | ||||
|   if (v == GL_SIGNALED) { | ||||
|     glDeleteSync(sync); | ||||
|     pro.Return({}); | ||||
|   } else { | ||||
|     ctx->env().ExecGL(ctx, [ctx, pro, sync]() mutable { | ||||
|       Await(ctx, pro, sync); | ||||
|     }, nf7::Env::Clock::now() + std::chrono::milliseconds(10)); | ||||
|   } | ||||
| } | ||||
|  | ||||
| // The returned future will be finalized on GL thread. | ||||
| inline nf7::Future<std::monostate> ExecFenceSync( | ||||
|     const std::shared_ptr<nf7::Context>& ctx) noexcept { | ||||
|   nf7::Future<std::monostate>::Promise pro {ctx}; | ||||
|  | ||||
|   ctx->env().ExecGL(ctx, [ctx, pro]() mutable { | ||||
|     GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); | ||||
|     assert(0 == glGetError()); | ||||
|     Await(ctx, pro, sync); | ||||
|   }); | ||||
|   return pro.future(); | ||||
| } | ||||
|  | ||||
| }  // namespace nf7::gl | ||||
							
								
								
									
										89
									
								
								common/gl_obj.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								common/gl_obj.hh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <cassert> | ||||
| #include <memory> | ||||
| #include <utility> | ||||
|  | ||||
| #include <GL/glew.h> | ||||
|  | ||||
| #include "nf7.hh" | ||||
|  | ||||
| #include "common/factory.hh" | ||||
| #include "common/future.hh" | ||||
| #include "common/mutex.hh" | ||||
|  | ||||
|  | ||||
| namespace nf7::gl { | ||||
|  | ||||
| template <typename T> | ||||
| class Obj final { | ||||
|  public: | ||||
|   Obj() = delete; | ||||
|   template <typename... Args> | ||||
|   Obj(const std::shared_ptr<nf7::Context>& ctx, GLuint id, Args&&... args) noexcept : | ||||
|       ctx_(ctx), id_(id? id: T::Gen()), meta_(std::forward<Args>(args)...) { | ||||
|   } | ||||
|   ~Obj() noexcept { | ||||
|     ctx_->env().ExecGL(ctx_, [id = id_]() { T::Delete(id); }); | ||||
|   } | ||||
|   Obj(const Obj&) = delete; | ||||
|   Obj(Obj&&) = delete; | ||||
|   Obj& operator=(const Obj&) = delete; | ||||
|   Obj& operator=(Obj&&) = delete; | ||||
|  | ||||
|   GLuint id() const noexcept { return id_; } | ||||
|  | ||||
|   T& meta() noexcept { return meta_; } | ||||
|   const T& meta() const noexcept { return meta_; } | ||||
|  | ||||
|  private: | ||||
|   std::shared_ptr<nf7::Context> ctx_; | ||||
|  | ||||
|   GLuint id_; | ||||
|   T meta_; | ||||
| }; | ||||
|  | ||||
|  | ||||
| struct Obj_BufferMeta final { | ||||
|  public: | ||||
|   Obj_BufferMeta() = delete; | ||||
|   Obj_BufferMeta(GLenum t) noexcept : type(t) { | ||||
|   } | ||||
|  | ||||
|   const GLenum type; | ||||
|  | ||||
|   size_t   size = 0; | ||||
|   uint8_t* ptr  = nullptr;  // address returned by glMapBuffer or nullptr | ||||
|  | ||||
|   static GLuint Gen() noexcept { | ||||
|     GLuint id; | ||||
|     glGenBuffers(1, &id); | ||||
|     return id; | ||||
|   } | ||||
|   static void Delete(GLuint id) noexcept { | ||||
|     glDeleteBuffers(1, &id); | ||||
|   } | ||||
| }; | ||||
| using Buffer        = Obj<Obj_BufferMeta>; | ||||
| using BufferFactory = AsyncFactory<nf7::Mutex::Resource<std::shared_ptr<Buffer>>>; | ||||
|  | ||||
|  | ||||
| struct Obj_TextureMeta final { | ||||
|  public: | ||||
|   GLenum type; | ||||
|   GLenum format; | ||||
|   uint32_t w, h, d; | ||||
|  | ||||
|   static GLuint Gen() noexcept { | ||||
|     GLuint id; | ||||
|     glGenTextures(1, &id); | ||||
|     return id; | ||||
|   } | ||||
|   static void Delete(GLuint id) noexcept { | ||||
|     glDeleteTextures(1, &id); | ||||
|   } | ||||
| }; | ||||
| using Texture        = Obj<Obj_TextureMeta>; | ||||
| using TextureFactory = AsyncFactory<nf7::Mutex::Resource<std::shared_ptr<Texture>>>; | ||||
|  | ||||
| }  // namespace nf7::gl | ||||
| @@ -12,7 +12,45 @@ namespace nf7 { | ||||
| template <typename T> | ||||
| class Life final { | ||||
|  public: | ||||
|   class Ref; | ||||
|   struct Data final { | ||||
|     std::atomic<T*> ptr; | ||||
|   }; | ||||
|   class Ref final { | ||||
|    public: | ||||
|     Ref() = default; | ||||
|     Ref(const Life& life) noexcept { | ||||
|       if (!life.data_) { | ||||
|         auto& l = const_cast<Life&>(life); | ||||
|         l.data_ = std::make_shared<Data>(); | ||||
|         l.data_->ptr = l.ptr_; | ||||
|       } | ||||
|       data_ = life.data_; | ||||
|     } | ||||
|     Ref(const Ref&) = default; | ||||
|     Ref(Ref&&) = default; | ||||
|     Ref& operator=(const Ref&) = default; | ||||
|     Ref& operator=(Ref&&) = default; | ||||
|  | ||||
|     void EnforceAlive() const { | ||||
|       if (!data_->ptr) { | ||||
|         throw nf7::ExpiredException {"target expired"}; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     operator bool() const noexcept { | ||||
|       return !!data_->ptr; | ||||
|     } | ||||
|     T& operator*() const noexcept { | ||||
|       assert(data_->ptr); | ||||
|       return *data_->ptr; | ||||
|     } | ||||
|     T* operator->() const noexcept { | ||||
|       return &**this; | ||||
|     } | ||||
|  | ||||
|    private: | ||||
|     std::shared_ptr<Data> data_; | ||||
|   }; | ||||
|  | ||||
|   Life() = delete; | ||||
|   Life(T& target) noexcept : ptr_(&target) { | ||||
| @@ -25,52 +63,14 @@ class Life final { | ||||
|   Life& operator=(const Life&) = delete; | ||||
|   Life& operator=(Life&&) = delete; | ||||
|  | ||||
|   Ref ref() const noexcept { return *this; } | ||||
|  | ||||
|  private: | ||||
|   T* const ptr_; | ||||
|  | ||||
|   struct Data final { | ||||
|     std::atomic<T*> ptr; | ||||
|   }; | ||||
|   std::shared_ptr<Data> data_; | ||||
| }; | ||||
|  | ||||
| template <typename T> | ||||
| class Life<T>::Ref final { | ||||
|  public: | ||||
|   Ref() = default; | ||||
|   Ref(const Life& life) noexcept { | ||||
|     if (!life.data_) { | ||||
|       auto& l = const_cast<Life&>(life); | ||||
|       l.data_ = std::make_shared<Data>(); | ||||
|       l.data_->ptr = l.ptr_; | ||||
|     } | ||||
|     data_ = life.data_; | ||||
|   } | ||||
|   Ref(const Ref&) = default; | ||||
|   Ref(Ref&&) = default; | ||||
|   Ref& operator=(const Ref&) = default; | ||||
|   Ref& operator=(Ref&&) = default; | ||||
|  | ||||
|   void EnforceAlive() const { | ||||
|     if (!data_->ptr) { | ||||
|       throw nf7::ExpiredException {"target expired"}; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   operator bool() const noexcept { | ||||
|     return !!data_->ptr; | ||||
|   } | ||||
|   T& operator*() const noexcept { | ||||
|     assert(data_->ptr); | ||||
|     return *data_->ptr; | ||||
|   } | ||||
|   T* operator->() const noexcept { | ||||
|     return &**this; | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   std::shared_ptr<Data> data_; | ||||
| }; | ||||
|  | ||||
|  | ||||
| }  // namespace nf7 | ||||
|   | ||||
							
								
								
									
										377
									
								
								file/gl_obj.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										377
									
								
								file/gl_obj.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,377 @@ | ||||
| #include <optional> | ||||
| #include <span> | ||||
| #include <string> | ||||
| #include <string_view> | ||||
| #include <typeinfo> | ||||
| #include <vector> | ||||
|  | ||||
| #include <imgui.h> | ||||
| #include <implot.h> | ||||
|  | ||||
| #include <magic_enum.hpp> | ||||
|  | ||||
| #include <yaml-cpp/yaml.h> | ||||
|  | ||||
| #include <yas/serialize.hpp> | ||||
| #include <yas/types/utility/usertype.hpp> | ||||
|  | ||||
| #include "nf7.hh" | ||||
|  | ||||
| #include "common/dir_item.hh" | ||||
| #include "common/factory.hh" | ||||
| #include "common/file_base.hh" | ||||
| #include "common/generic_context.hh" | ||||
| #include "common/generic_memento.hh" | ||||
| #include "common/generic_type_info.hh" | ||||
| #include "common/generic_watcher.hh" | ||||
| #include "common/gl_fence.hh" | ||||
| #include "common/gl_obj.hh" | ||||
| #include "common/gui_config.hh" | ||||
| #include "common/life.hh" | ||||
| #include "common/logger_ref.hh" | ||||
| #include "common/node.hh" | ||||
| #include "common/ptr_selector.hh" | ||||
| #include "common/yas_enum.hh" | ||||
|  | ||||
|  | ||||
| namespace nf7 { | ||||
| namespace { | ||||
|  | ||||
| template <typename T> | ||||
| class ObjBase : public nf7::FileBase, | ||||
|     public nf7::DirItem, public nf7::Node, | ||||
|     public nf7::AsyncFactory<nf7::Mutex::Resource<std::shared_ptr<typename T::Product>>> { | ||||
|  public: | ||||
|   using ThisObjBase     = ObjBase<T>; | ||||
|   using Product         = std::shared_ptr<typename T::Product>; | ||||
|   using Resource        = nf7::Mutex::Resource<Product>; | ||||
|   using ResourceFuture  = nf7::Future<Resource>; | ||||
|   using ResourcePromise = typename ResourceFuture::Promise; | ||||
|  | ||||
|   struct TypeInfo; | ||||
|  | ||||
|   static void UpdateTypeTooltip() noexcept { | ||||
|     T::UpdateTypeTooltip(); | ||||
|   } | ||||
|  | ||||
|   ObjBase(nf7::Env& env, T&& data = {}) noexcept : | ||||
|       nf7::FileBase(TypeInfo::kType, env, {&log_}), | ||||
|       nf7::DirItem(nf7::DirItem::kMenu | | ||||
|                    nf7::DirItem::kTooltip), | ||||
|       nf7::Node(nf7::Node::kNone), life_(*this), log_(*this), | ||||
|       mem_(std::move(data), *this) { | ||||
|     mem_.onRestore = mem_.onCommit = [this]() { | ||||
|       Drop(); | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   ObjBase(nf7::Deserializer& ar) : ObjBase(ar.env()) { | ||||
|     ar(mem_.data()); | ||||
|   } | ||||
|   void Serialize(nf7::Serializer& ar) const noexcept override { | ||||
|     ar(mem_.data()); | ||||
|   } | ||||
|   std::unique_ptr<nf7::File> Clone(nf7::Env& env) const noexcept override { | ||||
|     return std::make_unique<ThisObjBase>(env, T {mem_.data()}); | ||||
|   } | ||||
|  | ||||
|   std::shared_ptr<nf7::Node::Lambda> CreateLambda( | ||||
|       const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept override { | ||||
|     return std::make_shared<Lambda>(*this, parent); | ||||
|   } | ||||
|   std::span<const std::string> GetInputs() const noexcept override { | ||||
|     return T::kInputs; | ||||
|   } | ||||
|   std::span<const std::string> GetOutputs() const noexcept override { | ||||
|     return T::kOutputs; | ||||
|   } | ||||
|  | ||||
|   ResourceFuture Create() noexcept final { | ||||
|     return Create(false); | ||||
|   } | ||||
|   ResourceFuture Create(bool ex) noexcept { | ||||
|     auto ctx = std::make_shared<nf7::GenericContext>(*this, "OpenGL obj factory"); | ||||
|  | ||||
|     ResourcePromise pro {ctx}; | ||||
|     mtx_.AcquireLock(ctx, ex).ThenIf([this, ctx, pro](auto& k) mutable { | ||||
|       if (!fu_) { | ||||
|         watcher_.emplace(env()); | ||||
|         fu_ = mem_->Create(ctx, *watcher_); | ||||
|         watcher_->AddHandler(nf7::File::Event::kUpdate, [this](auto&) { Drop(); }); | ||||
|       } | ||||
|       fu_->ThenIf([pro, k](auto& obj) mutable { pro.Return({k, obj}); }); | ||||
|     }); | ||||
|     return pro.future(); | ||||
|   } | ||||
|  | ||||
|   void UpdateMenu() noexcept override { | ||||
|     if (ImGui::BeginMenu("object management")) { | ||||
|       if (ImGui::MenuItem("create", nullptr, false, !fu_)) { | ||||
|         Create(); | ||||
|       } | ||||
|       if (ImGui::MenuItem("drop", nullptr, false, !!fu_)) { | ||||
|         Drop(); | ||||
|       } | ||||
|       if (ImGui::MenuItem("drop and create")) { | ||||
|         Drop(); | ||||
|         Create(); | ||||
|       } | ||||
|       ImGui::EndMenu(); | ||||
|     } | ||||
|     if (ImGui::IsItemHovered()) { | ||||
|       ImGui::SetTooltip("these actions can cause CORRUPTION of running lambdas"); | ||||
|     } | ||||
|     if constexpr (nf7::gui::ConfigData<T>) { | ||||
|       if (ImGui::BeginMenu("config")) { | ||||
|         nf7::gui::Config(mem_); | ||||
|         ImGui::EndMenu(); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   void UpdateTooltip() noexcept override { | ||||
|     const char* status = "(unknown)"; | ||||
|     if (fu_) { | ||||
|       if (fu_->done()) { | ||||
|         status = "ready"; | ||||
|       } else if (fu_->error()) { | ||||
|         status = "error"; | ||||
|       } else { | ||||
|         status = "creating"; | ||||
|       } | ||||
|     } else { | ||||
|       status = "unused"; | ||||
|     } | ||||
|     ImGui::Text("status: %s", status); | ||||
|     ImGui::Spacing(); | ||||
|  | ||||
|     const auto prod = fu_ && fu_->done()? fu_->value(): nullptr; | ||||
|     mem_->UpdateTooltip(prod); | ||||
|   } | ||||
|  | ||||
|   nf7::File::Interface* interface(const std::type_info& t) noexcept override { | ||||
|     return nf7::InterfaceSelector< | ||||
|         nf7::DirItem, nf7::Memento, nf7::Node, | ||||
|         nf7::AsyncFactory<std::shared_ptr<T>>>(t).Select(this, &mem_); | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   nf7::Life<ThisObjBase>             life_; | ||||
|   nf7::LoggerRef                     log_; | ||||
|   std::optional<nf7::GenericWatcher> watcher_; | ||||
|  | ||||
|   nf7::Mutex mtx_; | ||||
|  | ||||
|   std::optional<nf7::Future<Product>> fu_; | ||||
|  | ||||
|   nf7::GenericMemento<T> mem_; | ||||
|  | ||||
|  | ||||
|   void Drop() noexcept { | ||||
|     auto ctx = std::make_shared<nf7::GenericContext>(*this, "dropping OpenGL obj"); | ||||
|     mtx_.AcquireLock(ctx, true  /* = exclusive */). | ||||
|         ThenIf([this](auto&) { | ||||
|           fu_ = std::nullopt; | ||||
|           Touch(); | ||||
|         }); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   class Lambda final : public nf7::Node::Lambda, | ||||
|       public std::enable_shared_from_this<ThisObjBase::Lambda> { | ||||
|    public: | ||||
|     Lambda(ThisObjBase& f, const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept : | ||||
|         nf7::Node::Lambda(f, parent), f_(f.life_) { | ||||
|     } | ||||
|  | ||||
|     void Handle(const nf7::Node::Lambda::Msg& in) noexcept final { | ||||
|       if (!f_) return; | ||||
|  | ||||
|       f_->Create(true  /* = exclusive */). | ||||
|           ThenIf(shared_from_this(), [this, in](auto& obj) { | ||||
|             try { | ||||
|               if (f_ && f_->mem_->Handle(shared_from_this(), obj, in)) { | ||||
|                 f_->Touch(); | ||||
|               } | ||||
|             } catch (nf7::Exception& e) { | ||||
|               f_->log_.Error(e); | ||||
|             } | ||||
|           }); | ||||
|     } | ||||
|  | ||||
|    private: | ||||
|     using std::enable_shared_from_this<Lambda>::shared_from_this; | ||||
|  | ||||
|     nf7::Life<ThisObjBase>::Ref f_; | ||||
|   }; | ||||
| }; | ||||
|  | ||||
|  | ||||
| struct Buffer { | ||||
|  public: | ||||
|   static void UpdateTypeTooltip() noexcept { | ||||
|     ImGui::TextUnformatted("OpenGL buffer"); | ||||
|   } | ||||
|  | ||||
|   static inline const std::vector<std::string> kInputs = { | ||||
|     "upload", | ||||
|   }; | ||||
|   static inline const std::vector<std::string> kOutputs = { | ||||
|   }; | ||||
|  | ||||
|   using Product = nf7::gl::Buffer; | ||||
|  | ||||
|   enum class Type { | ||||
|     Array, | ||||
|     Element, | ||||
|   }; | ||||
|   enum class Usage { | ||||
|     StaticDraw, | ||||
|     DynamicDraw, | ||||
|     StreamDraw, | ||||
|     StaticRead, | ||||
|     DynamicRead, | ||||
|     StreamRead, | ||||
|     StaticCopy, | ||||
|     DynamicCopy, | ||||
|     StreamCopy, | ||||
|   }; | ||||
|  | ||||
|   Buffer() = default; | ||||
|   Buffer(const Buffer&) = default; | ||||
|   Buffer(Buffer&&) = default; | ||||
|   Buffer& operator=(const Buffer&) = default; | ||||
|   Buffer& operator=(Buffer&&) = default; | ||||
|  | ||||
|   void serialize(auto& ar) { | ||||
|     ar(type_, usage_); | ||||
|   } | ||||
|  | ||||
|   std::string Stringify() noexcept { | ||||
|     YAML::Emitter st; | ||||
|     st << YAML::BeginMap; | ||||
|     st << YAML::Key   << "type"; | ||||
|     st << YAML::Value << std::string {magic_enum::enum_name(type_)}; | ||||
|     st << YAML::Key   << "usage"; | ||||
|     st << YAML::Value << std::string {magic_enum::enum_name(usage_)}; | ||||
|     st << YAML::EndMap; | ||||
|     return std::string {st.c_str(), st.size()}; | ||||
|   } | ||||
|  | ||||
|   void Parse(const std::string& v) | ||||
|   try { | ||||
|     const auto yaml = YAML::Load(v); | ||||
|  | ||||
|     const auto new_type = magic_enum:: | ||||
|         enum_cast<Type>(yaml["type"].as<std::string>()).value(); | ||||
|     const auto new_usage = magic_enum:: | ||||
|         enum_cast<Usage>(yaml["usage"].as<std::string>()).value(); | ||||
|  | ||||
|     type_  = new_type; | ||||
|     usage_ = new_usage; | ||||
|   } catch (std::bad_optional_access&) { | ||||
|     throw nf7::Exception {"unknown enum"}; | ||||
|   } catch (YAML::Exception& e) { | ||||
|     throw nf7::Exception {std::string {"YAML error: "}+e.what()}; | ||||
|   } | ||||
|  | ||||
|   nf7::Future<std::shared_ptr<Product>> Create( | ||||
|       const std::shared_ptr<nf7::Context>& ctx, nf7::Env::Watcher&) noexcept { | ||||
|     nf7::Future<std::shared_ptr<Product>>::Promise pro {ctx}; | ||||
|     ctx->env().ExecGL(ctx, [ctx, pro, t = type_]() mutable { | ||||
|       pro.Return(std::make_shared<Product>(ctx, GLuint {0}, FromType(t))); | ||||
|     }); | ||||
|     return pro.future(); | ||||
|   } | ||||
|  | ||||
|   bool Handle(const std::shared_ptr<nf7::Node::Lambda>&             handler, | ||||
|               const nf7::Mutex::Resource<std::shared_ptr<Product>>& res, | ||||
|               const nf7::Node::Lambda::Msg&                         in) { | ||||
|     if (in.name == "upload") { | ||||
|       const auto& vec = in.value.vector(); | ||||
|       if (vec->size() == 0) return false; | ||||
|  | ||||
|       const auto usage = FromUsage(usage_); | ||||
|       handler->env().ExecGL(handler, [res, vec, usage]() { | ||||
|         const auto n = static_cast<GLsizeiptr>(vec->size()); | ||||
|  | ||||
|         auto& buf = **res; | ||||
|         auto& m   = buf.meta(); | ||||
|         glBindBuffer(m.type, buf.id()); | ||||
|         { | ||||
|           if (m.size != vec->size()) { | ||||
|             m.size = vec->size(); | ||||
|             glBufferData(m.type, n, vec->data(), usage); | ||||
|           } else { | ||||
|             glBufferSubData(m.type, 0, n, vec->data()); | ||||
|           } | ||||
|         } | ||||
|         glBindBuffer(m.type, 0); | ||||
|         assert(0 == glGetError()); | ||||
|       }); | ||||
|       return true; | ||||
|     } else { | ||||
|       throw nf7::Exception {"unknown input: "+in.name}; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void UpdateTooltip(const std::shared_ptr<Product>& prod) noexcept { | ||||
|     const auto t = magic_enum::enum_name(type_); | ||||
|     ImGui::Text("type: %.*s", static_cast<int>(t.size()), t.data()); | ||||
|     if (prod) { | ||||
|       ImGui::Text("  id: %zu", static_cast<size_t>(prod->id())); | ||||
|       ImGui::Text("size: %zu bytes", prod->meta().size); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   Type  type_  = Type::Array; | ||||
|   Usage usage_ = Usage::StaticDraw; | ||||
|  | ||||
|   static GLenum FromType(Type t) { | ||||
|     return | ||||
|         t == Type::Array?   GL_ARRAY_BUFFER: | ||||
|         t == Type::Element? GL_ELEMENT_ARRAY_BUFFER: | ||||
|         throw 0; | ||||
|   } | ||||
|   static GLenum FromUsage(Usage u) { | ||||
|     return | ||||
|         u == Usage::StaticDraw?  GL_STATIC_DRAW: | ||||
|         u == Usage::DynamicDraw? GL_DYNAMIC_DRAW: | ||||
|         u == Usage::StreamDraw?  GL_STREAM_DRAW: | ||||
|         u == Usage::StaticRead?  GL_STATIC_READ: | ||||
|         u == Usage::DynamicRead? GL_DYNAMIC_READ: | ||||
|         u == Usage::StreamRead?  GL_STREAM_READ: | ||||
|         u == Usage::StaticCopy?  GL_STATIC_COPY: | ||||
|         u == Usage::DynamicCopy? GL_DYNAMIC_COPY: | ||||
|         u == Usage::StreamCopy?  GL_STREAM_COPY: | ||||
|         throw 0; | ||||
|   } | ||||
| }; | ||||
| template <> | ||||
| struct ObjBase<Buffer>::TypeInfo final { | ||||
|   static inline const nf7::GenericTypeInfo<ObjBase<Buffer>> kType = {"GL/Buffer", {"nf7::DirItem"}}; | ||||
| }; | ||||
|  | ||||
| } | ||||
| }  // namespace nf7 | ||||
|  | ||||
|  | ||||
| namespace yas::detail { | ||||
|  | ||||
| template <size_t F> | ||||
| struct serializer< | ||||
|     yas::detail::type_prop::is_enum, | ||||
|     yas::detail::ser_case::use_internal_serializer, | ||||
|     F, nf7::Buffer::Type> : | ||||
|         nf7::EnumSerializer<nf7::Buffer::Type> { | ||||
| }; | ||||
|  | ||||
| template <size_t F> | ||||
| struct serializer< | ||||
|     yas::detail::type_prop::is_enum, | ||||
|     yas::detail::ser_case::use_internal_serializer, | ||||
|     F, nf7::Buffer::Usage> : | ||||
|         nf7::EnumSerializer<nf7::Buffer::Usage> { | ||||
| }; | ||||
|  | ||||
| }  // namespace yas::detail | ||||
		Reference in New Issue
	
	Block a user