t5deRenderEngine.dll implements IMVU Classic's renderer plugin interface by wrapping IMVU's own OpenGL renderer, and logs what each frame calls, to prove our declarations of the interface. Built with CMake presets for 32-bit MSVC; docs/interface.md has the findings. Co-Authored-By: Claude Opus 5.5 <[email protected]>
130 lines
3.8 KiB
C++
130 lines
3.8 KiB
C++
#pragma once
|
|
// boost::shared_ptr as IMVU's DLLs lay it out (Boost 1.61, Visual C++ 2010, 32-bit): the object pointer, then a
|
|
// pointer to the control block. Shared pointers cross the plugin boundary in both directions, so ours must be
|
|
// interchangeable with theirs: same layout, same control block vtable, same atomic counts.
|
|
|
|
#include <intrin.h>
|
|
|
|
#include <memory>
|
|
#include <typeinfo>
|
|
#include <utility>
|
|
|
|
namespace t5de::imvu {
|
|
|
|
// boost::detail::sp_counted_base: its virtual methods, in this order, then the use and weak counts.
|
|
class CountedBase {
|
|
public:
|
|
CountedBase() = default;
|
|
CountedBase(const CountedBase&) = delete;
|
|
CountedBase& operator=(const CountedBase&) = delete;
|
|
|
|
virtual ~CountedBase() = default;
|
|
// Destroys the object; called when the use count reaches 0.
|
|
virtual void dispose() noexcept = 0;
|
|
// Destroys the control block; called when the weak count reaches 0.
|
|
virtual void destroy() noexcept { delete this; }
|
|
virtual void* get_deleter(const std::type_info&) noexcept { return nullptr; }
|
|
virtual void* get_untyped_deleter() noexcept { return nullptr; }
|
|
|
|
void add_ref() noexcept { _InterlockedIncrement(&use_); }
|
|
|
|
void release() noexcept
|
|
{
|
|
if (_InterlockedDecrement(&use_) == 0) {
|
|
dispose();
|
|
weak_release();
|
|
}
|
|
}
|
|
|
|
void weak_release() noexcept
|
|
{
|
|
if (_InterlockedDecrement(&weak_) == 0) {
|
|
destroy();
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] long use_count() const noexcept { return use_; }
|
|
|
|
private:
|
|
long use_ = 1;
|
|
long weak_ = 1;
|
|
};
|
|
|
|
static_assert(sizeof(CountedBase) == 12, "vtable pointer, use count, weak count");
|
|
|
|
// A control block owning a heap object, deleted with delete (boost's sp_counted_impl_p).
|
|
template <class T>
|
|
class CountedPointer final : public CountedBase {
|
|
public:
|
|
explicit CountedPointer(T* object) noexcept : object_(object) {}
|
|
void dispose() noexcept override { delete object_; }
|
|
|
|
private:
|
|
T* object_;
|
|
};
|
|
|
|
template <class T>
|
|
class SharedPtr {
|
|
public:
|
|
SharedPtr() noexcept = default;
|
|
|
|
explicit SharedPtr(std::unique_ptr<T> object) : object_(object.get())
|
|
{
|
|
if (object_ != nullptr) {
|
|
counts_ = new CountedPointer<T>(object_);
|
|
object.release();
|
|
}
|
|
}
|
|
|
|
// Shares ownership of a derived object (the counts stay with the object's own control block).
|
|
template <class U>
|
|
requires std::is_convertible_v<U*, T*>
|
|
SharedPtr(SharedPtr<U> other) noexcept // NOLINT(google-explicit-constructor): like boost's
|
|
: object_(std::exchange(other.object_, nullptr)), counts_(std::exchange(other.counts_, nullptr))
|
|
{
|
|
}
|
|
|
|
SharedPtr(const SharedPtr& other) noexcept : object_(other.object_), counts_(other.counts_)
|
|
{
|
|
if (counts_ != nullptr) {
|
|
counts_->add_ref();
|
|
}
|
|
}
|
|
|
|
SharedPtr(SharedPtr&& other) noexcept
|
|
: object_(std::exchange(other.object_, nullptr)), counts_(std::exchange(other.counts_, nullptr))
|
|
{
|
|
}
|
|
|
|
SharedPtr& operator=(SharedPtr other) noexcept
|
|
{
|
|
std::swap(object_, other.object_);
|
|
std::swap(counts_, other.counts_);
|
|
return *this;
|
|
}
|
|
|
|
~SharedPtr()
|
|
{
|
|
if (counts_ != nullptr) {
|
|
counts_->release();
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] T* get() const noexcept { return object_; }
|
|
T* operator->() const noexcept { return object_; }
|
|
T& operator*() const noexcept { return *object_; }
|
|
explicit operator bool() const noexcept { return object_ != nullptr; }
|
|
[[nodiscard]] long use_count() const noexcept { return counts_ != nullptr ? counts_->use_count() : 0; }
|
|
|
|
private:
|
|
template <class U>
|
|
friend class SharedPtr;
|
|
|
|
T* object_ = nullptr;
|
|
CountedBase* counts_ = nullptr;
|
|
};
|
|
|
|
static_assert(sizeof(SharedPtr<int>) == 8, "object pointer, control block pointer");
|
|
|
|
} // namespace t5de::imvu
|