Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions include/RED4ext/Rendering/RenderObject.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#pragma once

#include <RED4ext/Memory/Utils.hpp>

namespace RED4ext
{
struct IRenderObject
{
template<std::derived_from<IRenderObject> T>
friend class TRenderPtr;

using AllocatorType = Memory::RenderDataAllocator;

virtual Memory::IAllocator* GetAllocator()
{
return AllocatorType::Get();
}

virtual void Destroy()
{
if (this)
{
Memory::Delete(this);
}
}

virtual ~IRenderObject() = default;

protected:
void Release()
{
if (--m_refCount < 1)
Destroy();
}

void AddRef()
{
m_refCount++;
}

std::atomic<int32_t> m_refCount = 1;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this class is reversed from tha game, then shouldn't this use SpinLock.hpp?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That wouldn't match the sizing, so I don't think so. It could still be something different, but std::atomic achieves the exact same thing in its place afaik, so why not use it?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because this way we depend on the STL. While they don't do ABI breaking changes now, we are not sure what might happen in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, leave that for the day we implement atomics as the engine does (which probably still use STL underneath anyway)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about this then: https://github.com/wopss/RED4ext.SDK/blob/c7d0481e725b913e6a2fbd11f8780a070287fab2/include/RED4ext/Memory/SharedPtr.hpp#L15C8-L15C14 ?

If not, then add a // TODO: comment, but I'm still leaning to fix it, even using plain InterlockedIncrement. @psiberx wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That still doesn't match the sizing... the code shows atomic operations for a signed dword (int32)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not RefCnt. But I also feel like a direct implementation with InterlockedIncrement is better than using std.

Copy link
Contributor Author

@Mozz3d Mozz3d Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we'd prefer something like this?

void Release()
{
   if (InterlockedDecrement(&m_refCount) == 0)
   {
        Destroy();
    }
}
void AddRef()
{
    InterlockedIncrement(&m_refCount);
}
long m_refCount{1};

};
RED4EXT_ASSERT_SIZE(IRenderObject, 0x10);

template<std::derived_from<IRenderObject> T = IRenderObject>
class TRenderPtr
{
public:
TRenderPtr() = default;

~TRenderPtr()
{
Release();
}

TRenderPtr(std::nullptr_t) noexcept
{
}

explicit TRenderPtr(T* aPointer) noexcept
: m_instance(aPointer)
{
}

TRenderPtr(const TRenderPtr& aOther) noexcept
: m_instance(aOther.m_instance)
{
if (m_instance)
m_instance->AddRef();
}

TRenderPtr(TRenderPtr&& aOther) noexcept
{
Swap(aOther);
}

explicit operator bool() const noexcept
{
return m_instance != nullptr;
}

TRenderPtr& operator=(const TRenderPtr& aRhs) noexcept
{
TRenderPtr(aRhs).Swap(*this);
return *this;
}

TRenderPtr& operator=(TRenderPtr&& aRhs) noexcept
{
Swap(aRhs);
return *this;
}

T* operator->() const noexcept
{
return m_instance;
}

T& operator*() const noexcept
{
return *m_instance;
}

void Swap(TRenderPtr& aOther) noexcept
{
std::swap(m_instance, aOther.m_instance);
}

T* GetPtr() const noexcept
{
return m_instance;
}

private:
void Release()
{
if (m_instance)
m_instance->Release();
}

T* m_instance = nullptr;
};
RED4EXT_ASSERT_SIZE(TRenderPtr<>, 0x08);
} // namespace RED4ext
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#pragma once

#ifdef RED4EXT_STATIC_LIB
#include <RED4ext/RenderProxy.hpp>
#include <RED4ext/Rendering/RenderProxy.hpp>
#endif

#include <RED4ext/Detail/AddressHashes.hpp>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#pragma once

#include <RED4ext/Common.hpp>
#include <RED4ext/RenderResource.hpp>
#include <RED4ext/Rendering/RenderResource.hpp>
#include <RED4ext/Scripting/Natives/Generated/IRenderProxyCustomData.hpp>

#include <cstdint>
Expand Down Expand Up @@ -83,3 +83,7 @@ struct CRenderProxyHandle
RED4EXT_ASSERT_SIZE(CRenderProxyHandle, 0x28);
RED4EXT_ASSERT_OFFSET(CRenderProxyHandle, renderProxy, 0x10);
} // namespace RED4ext

#ifdef RED4EXT_HEADER_ONLY
#include <RED4ext/Rendering/RenderProxy-inl.hpp>
#endif
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
#pragma once

#include <RED4ext/Common.hpp>
#include <RED4ext/Rendering/RenderObject.hpp>
#include <RED4ext/Scripting/Natives/Generated/rend/Chunk.hpp>
#include <RED4ext/Scripting/Natives/Vector4.hpp>

#include <cstdint>

namespace RED4ext
{
struct CRenderMesh
struct IRenderResource : IRenderObject
{
using AllocatorType = Memory::RenderResourcesAllocator;

virtual ~IRenderResource() = default;
virtual CName GetResourceName() = 0;
virtual char** sub_20() = 0;
virtual int32_t sub_28() = 0;
};
RED4EXT_ASSERT_SIZE(IRenderResource, 0x10);

struct CRenderMesh : IRenderResource
{
uint8_t unk00[0x10 - 0x00]; // 00
Vector4 quantizationScale; // 10
Vector4 quantizationBias; // 20
uint32_t vertexBufferID; // 30 - GpuApi buffer ID
Expand Down
2 changes: 1 addition & 1 deletion include/RED4ext/Scripting/Natives/CMesh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include <RED4ext/DynArray.hpp>
#include <RED4ext/Handle.hpp>
#include <RED4ext/NativeTypes.hpp>
#include <RED4ext/RenderResource.hpp>
#include <RED4ext/Rendering/RenderResource.hpp>
#include <RED4ext/Scripting/Natives/Generated/Box.hpp>
#include <RED4ext/Scripting/Natives/Generated/CMeshMaterialEntry.hpp>
#include <RED4ext/Scripting/Natives/Generated/ERenderObjectType.hpp>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#include <RED4ext/CName.hpp>
#include <RED4ext/Common.hpp>
#include <RED4ext/NativeTypes.hpp>
#include <RED4ext/RenderProxy.hpp>
#include <RED4ext/Rendering/RenderProxy.hpp>
#include <RED4ext/Scripting/Natives/Generated/ent/ISkinTargetComponent.hpp>
#include <RED4ext/Scripting/Natives/Generated/red/TagList.hpp>
#include <RED4ext/Scripting/Natives/Generated/shadows/ShadowCastingMode.hpp>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#include <RED4ext/CName.hpp>
#include <RED4ext/Common.hpp>
#include <RED4ext/NativeTypes.hpp>
#include <RED4ext/RenderProxy.hpp>
#include <RED4ext/Rendering/RenderProxy.hpp>
#include <RED4ext/Scripting/Natives/Generated/CMesh.hpp>
#include <RED4ext/Scripting/Natives/Generated/NavGenNavigationSetting.hpp>
#include <RED4ext/Scripting/Natives/Generated/ent/ForcedLodDistance.hpp>
Expand Down
2 changes: 1 addition & 1 deletion src/RenderProxy.cpp → src/Rendering/RenderProxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
#error Please define 'RED4EXT_STATIC_LIB' to compile this file.
#endif

#include <RED4ext/RenderProxy-inl.hpp>
#include <RED4ext/Rendering/RenderProxy-inl.hpp>