diff --git a/source/core/3d/Material.hpp b/source/core/3d/Material.hpp index a41c62342..d7fbed639 100644 --- a/source/core/3d/Material.hpp +++ b/source/core/3d/Material.hpp @@ -7,6 +7,8 @@ #include // std::string #include // std::pair +#include + namespace librii::glhelper { class DelegatedUBOBuilder; } @@ -17,15 +19,12 @@ struct Material; class Scene; class Model; -struct IObserver { - virtual ~IObserver() = default; - // TODO: Detach - virtual void update(Material* mat) {} - - IObserver() = default; - IObserver(const IObserver&) = delete; - IObserver(IObserver&&) = delete; +struct MaterialEvents { + virtual void update(Material* mat) = 0; }; + +using IObserver = typename rsl::Subscriber; + struct Polygon; struct Material : public virtual kpi::IObject { virtual ~Material() = default; @@ -44,13 +43,10 @@ struct Material : public virtual kpi::IObject { std::vector& textures) = 0; // TODO: Better system.. - void notifyObservers() { - for (auto* it : observers) { - assert(it != nullptr); - it->update(this); - } - } + void notifyObservers() { observers.publish(&MaterialEvents::update, this); } void onUpdate() { + nextGenerationId(); + // (for shader recompilation) notifyObservers(); } @@ -62,12 +58,25 @@ struct Material : public virtual kpi::IObject { applyCacheAgain = rhs.applyCacheAgain; return *this; } - mutable std::vector observers; + mutable rsl::PubSubChannel observers; mutable std::string cachedPixelShader; mutable bool isShaderError = false; mutable std::string shaderError; mutable bool applyCacheAgain = false; + + // Updating this value will force a cache invalidation. However, perhaps not + // all changes warrant a cache invalidation. If you had the base type, you + // could hold onto a copy of the old texture to determine if said update is + // necessary; but in that case, you wouldn't need this system anyway. + // + // The other issue is that it's up to the user to update the generation ID. + // However, that is also not resolved by using the observer system. + // + virtual s32 getGenerationId() const { return mGenerationId; } + virtual void nextGenerationId() { ++mGenerationId; } + + s32 mGenerationId = 0; }; } // namespace riistudio::lib3d diff --git a/source/frontend/properties/gc/Material/Common.hpp b/source/frontend/properties/gc/Material/Common.hpp index ee14ead66..bdb6a875f 100644 --- a/source/frontend/properties/gc/Material/Common.hpp +++ b/source/frontend/properties/gc/Material/Common.hpp @@ -11,7 +11,7 @@ before, after, [&](const auto& x) { return x.val; }, \ [&](auto& x, auto& y) { \ x.val = y; \ - x.notifyObservers(); \ + x.onUpdate(); \ }) #define AUTO_PROP(before, after) \ @@ -83,8 +83,8 @@ SamplerCombo(int current, auto sid = std::string("Sampler ID"_j) + "##Img"; if (ImGui::BeginCombo(sid.c_str(), samplers.empty() - ? "No Samplers"_j - : format(current).c_str())) { + ? "No Samplers"_j + : format(current).c_str())) { if (allow_none) { bool selected = current = 0xff; if (ImGui::Selectable("None"_j, selected)) { diff --git a/source/librii/g3d/gfx/G3dGfx.hpp b/source/librii/g3d/gfx/G3dGfx.hpp index efbbe07be..8dab5576d 100644 --- a/source/librii/g3d/gfx/G3dGfx.hpp +++ b/source/librii/g3d/gfx/G3dGfx.hpp @@ -29,6 +29,27 @@ AddPolygonToVBO(librii::glhelper::VBOBuilder& vbo_builder, return poly.propagate(mdl, mp_id, vbo_builder); } +struct ShaderCompileError { + std::string desc; +}; + +inline std::variant +CompileMaterial(const lib3d::Material& _mat) { + DebugReport("Compiling shader for %s..\n", _mat.getName().c_str()); + const auto shader_sources = _mat.generateShaders(); + librii::glhelper::ShaderProgram new_shader( + shader_sources.first, + _mat.applyCacheAgain ? _mat.cachedPixelShader : shader_sources.second); + if (new_shader.getError()) { + _mat.isShaderError = true; + _mat.shaderError = new_shader.getErrorDesc(); + return ShaderCompileError{.desc = _mat.shaderError}; + } + _mat.isShaderError = false; + + return new_shader; +} + struct CompiledLib3dTexture { CompiledLib3dTexture() = default; CompiledLib3dTexture(const lib3d::Texture& tex) @@ -100,7 +121,7 @@ struct ObservableShader { auto& getProgram() { return mImpl->mProgram; } void attachToMaterial(const lib3d::Material& mat) { - mat.observers.push_back(mImpl.get()); + mImpl->subscribe(mat.observers); } private: @@ -112,18 +133,18 @@ struct ObservableShader { librii::glhelper::ShaderProgram mProgram; void update(lib3d::Material* _mat) final { - DebugReport("Recompiling shader for %s..\n", _mat->getName().c_str()); - const auto shader_sources = _mat->generateShaders(); - librii::glhelper::ShaderProgram new_shader( - shader_sources.first, _mat->applyCacheAgain ? _mat->cachedPixelShader - : shader_sources.second); - if (new_shader.getError()) { - _mat->isShaderError = true; - _mat->shaderError = new_shader.getErrorDesc(); + assert(_mat != nullptr); + + auto result = CompileMaterial(*_mat); + if (auto* shader = std::get_if(&result); + shader != nullptr) { + mProgram = std::move(*shader); return; } - mProgram = std::move(new_shader); - _mat->isShaderError = false; + + if (auto* err = std::get_if(&result); + err != nullptr) { + } } }; std::unique_ptr mImpl; @@ -160,27 +181,6 @@ struct GenericShaderCache_WithObserverUpdates { } }; -struct ShaderCompileError { - std::string desc; -}; - -inline std::variant -CompileMaterial(const lib3d::Material* _mat) { - DebugReport("Compiling shader for %s..\n", _mat->getName().c_str()); - const auto shader_sources = _mat->generateShaders(); - librii::glhelper::ShaderProgram new_shader( - shader_sources.first, - _mat->applyCacheAgain ? _mat->cachedPixelShader : shader_sources.second); - if (new_shader.getError()) { - _mat->isShaderError = true; - _mat->shaderError = new_shader.getErrorDesc(); - return ShaderCompileError{.desc = _mat->shaderError}; - } - _mat->isShaderError = false; - - return new_shader; -} - struct G3dShaderCache_WithUnusableHashingMechanism { using MatData = riistudio::g3d::G3dMaterialData; @@ -203,7 +203,7 @@ struct G3dShaderCache_WithUnusableHashingMechanism { if (it != data.end()) return *it->second; - auto result = CompileMaterial(&mat); + auto result = CompileMaterial(mat); if (auto* shader = std::get_if(&result); shader != nullptr) {