Program Listing for File AsyncModelParse.ixx

Program Listing for File AsyncModelParse.ixx#

Return to documentation for file (Src/GraphicsEngineVulkan/scene/AsyncModelParse.ixx)

module;
#include <atomic>
#include <memory>
#include <string>
#include <thread>

export module kataglyphis.vulkan.async_model_parse;

import kataglyphis.vulkan.gltf_loader;
import kataglyphis.vulkan.obj_loader;
import kataglyphis.vulkan.model_file_kind;

export namespace Kataglyphis {

class AsyncModelParse
{
  public:
    AsyncModelParse() = default;
    AsyncModelParse(const AsyncModelParse &) = delete;
    AsyncModelParse &operator=(const AsyncModelParse &) = delete;

    ~AsyncModelParse() { waitForCompletion(); }

    void start(const std::string &modelFile)
    {
        waitForCompletion();

        finished.store(false, std::memory_order_release);
        succeeded.store(false, std::memory_order_release);
        loader.reset();
        gltfLoader.reset();
        path = modelFile;

        // Dispatch by extension. The OBJ path is unchanged; glTF gets a parallel
        // one so both load off the render thread.
        if (isGltfModelPath(modelFile)) {
            gltfLoader = std::make_unique<GltfLoader>();// device-free
            worker = std::thread([this] {
                const bool ok = gltfLoader->parseCpu(path);
                succeeded.store(ok, std::memory_order_release);
                finished.store(true, std::memory_order_release);
            });
        } else {
            loader = std::make_unique<ObjLoader>();// device-free
            worker = std::thread([this] {
                const bool ok = loader->parseCpu(path);
                succeeded.store(ok, std::memory_order_release);
                // Release LAST: a reader that sees finished == true must also see
                // every write the parse made, including `succeeded`.
                finished.store(true, std::memory_order_release);
            });
        }
    }

    [[nodiscard]] bool isRunning() const { return worker.joinable() && !isFinished(); }

    [[nodiscard]] bool isFinished() const { return finished.load(std::memory_order_acquire); }

    [[nodiscard]] bool wasSuccessful() const { return succeeded.load(std::memory_order_acquire); }

    std::unique_ptr<ObjLoader> takeResult()
    {
        waitForCompletion();
        if (!succeeded.load(std::memory_order_acquire)) {
            loader.reset();
            return nullptr;
        }
        return std::move(loader);
    }

    [[nodiscard]] bool parsedGltf() const { return gltfLoader != nullptr; }

    std::unique_ptr<GltfLoader> takeGltfResult()
    {
        waitForCompletion();
        if (!succeeded.load(std::memory_order_acquire)) {
            gltfLoader.reset();
            return nullptr;
        }
        return std::move(gltfLoader);
    }

    void waitForCompletion()
    {
        if (worker.joinable()) { worker.join(); }
    }

  private:
    std::thread worker;
    std::atomic<bool> finished{ false };
    std::atomic<bool> succeeded{ false };
    std::unique_ptr<ObjLoader> loader;
    std::unique_ptr<GltfLoader> gltfLoader;
    std::string path;
};

}// namespace Kataglyphis