You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/CryEngine/RenderDll/Common/DevBuffer.h

524 lines
14 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include <XRenderD3D9/DeviceManager/Base.h>
#include <XRenderD3D9/DeviceManager/Enums.h>
////////////////////////////////////////////////////////////////////////////////////////
// Usage hints
enum BUFFER_USAGE
{
BU_IMMUTABLE = 0, // For data that never, ever changes
BU_STATIC, // For long-lived data that changes infrequently (every n-frames)
BU_DYNAMIC, // For short-lived data that changes frequently (every frame)
BU_TRANSIENT, // For very short-lived data that can be considered garbage after first usage
BU_TRANSIENT_RT, // For very short-lived data that can be considered garbage after first usage
BU_WHEN_LOADINGTHREAD_ACTIVE, // yes we can ... because renderloadingthread frames not synced with mainthread frames
BU_MAX
};
////////////////////////////////////////////////////////////////////////////////////////
// Binding flags
enum BUFFER_BIND_TYPE
{
BBT_VERTEX_BUFFER = 0,
BBT_INDEX_BUFFER,
BBT_MAX
};
typedef uintptr_t buffer_handle_t;
typedef uint32 item_handle_t;
// CRY DX12
////////////////////////////////////////////////////////////////////////////////////////
struct SDescriptorBlock
{
SDescriptorBlock(uint32 id)
: blockID(id)
, pBuffer(NULL)
, size(0)
, offset(~0u)
{}
const uint32 blockID;
void* pBuffer;
uint32 size;
uint32 offset;
};
class CDeviceBufferManager;
struct ConstantBufferAllocator;
namespace AzRHI
{
enum class ConstantBufferUsage : AZ::u8
{
Static,
Dynamic
};
enum class ConstantBufferFlags : AZ::u8
{
None = 0,
DenyStreaming = BIT(1), // Used by OpenGL for constant buffer streaming
};
AZ_DEFINE_ENUM_BITWISE_OPERATORS(ConstantBufferFlags)
class ConstantBuffer
{
public:
ConstantBuffer(uint32 handle);
virtual ~ConstantBuffer();
inline D3DBuffer* GetPlatformBuffer() const
{
return m_buffer;
}
inline AzRHI::ConstantBufferUsage GetUsage() const
{
return m_usage;
}
inline AzRHI::ConstantBufferFlags GetFlags() const
{
return m_flags;
}
inline AZ::u32 GetByteOffset() const
{
return m_offset;
}
inline AZ::u32 GetByteCount() const
{
return m_size;
}
#if !defined(NULL_RENDERER)
inline AZ::u64 GetCode() const
{
#if defined(AZ_RESTRICTED_PLATFORM)
#include AZ_RESTRICTED_FILE(DevBuffer_h)
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#else
return reinterpret_cast<AZ::u64>(m_buffer) | ((AZ::u64)m_offset << 40);
#endif
}
#endif // !NULL_RENDERER
void AddRef();
AZ::u32 Release();
void* BeginWrite();
void EndWrite();
void UpdateBuffer(const void* data, AZ::u32 size);
private:
friend class ::CDeviceBufferManager;
friend struct ::ConstantBufferAllocator;
const char* m_name;
D3DBuffer* m_buffer;
item_handle_t m_handle;
void* m_allocator;
void* m_base_ptr;
AZ::u32 m_offset;
AZ::u32 m_size;
ConstantBufferUsage m_usage;
ConstantBufferFlags m_flags;
AZ::u8 m_used : 1;
AZ::u8 m_dynamic : 1;
AZStd::atomic_uint m_refCount;
int m_nHeapOffset;
SDescriptorBlock* m_pDescriptorBlock;
};
using ConstantBufferPtr = _smart_ptr<AzRHI::ConstantBuffer>;
AZ::u32 GetConstantRegisterCountMax(EHWShaderClass shaderClass);
}
////////////////////////////////////////////////////////////////////////////////////////
// Pool statistics
struct SDeviceBufferPoolStats
: private NoCopy
{
string buffer_descr;
size_t bank_size; // size of a pool bank in bytes
size_t num_banks; // number of banks currently allocated
size_t num_allocs; // number of allocs present in the device pool
IDefragAllocatorStats allocator_stats; // backing allocator statistics
SDeviceBufferPoolStats()
: buffer_descr()
, bank_size()
, num_banks()
, num_allocs()
, allocator_stats()
{ memset(&allocator_stats, 0x0, sizeof(allocator_stats)); }
~SDeviceBufferPoolStats() {}
};
class CVertexBuffer;
class CIndexBuffer;
class IDeviceBufferManager
{
friend class CGuardedDeviceBufferManager;
friend class CDeviceManager;
public:
# ifndef NULL_RENDERER
virtual D3DBuffer* GetD3D(buffer_handle_t handle, size_t* outOffset) = 0;
#endif
virtual void LockDevMan() = 0;
virtual void UnlockDevMan() = 0;
private:
virtual buffer_handle_t Create_Locked(BUFFER_BIND_TYPE, BUFFER_USAGE, size_t) = 0;
virtual void Destroy_Locked(buffer_handle_t) = 0;
virtual void* BeginRead_Locked(buffer_handle_t handle) = 0;
virtual void* BeginWrite_Locked(buffer_handle_t handle) = 0;
virtual void EndReadWrite_Locked(buffer_handle_t handle) = 0;
virtual bool UpdateBuffer_Locked(buffer_handle_t handle, const void*, size_t) = 0;
virtual size_t Size_Locked(buffer_handle_t) = 0;
};
class CDeviceBufferManager : public IDeviceBufferManager
{
buffer_handle_t Create_Locked(BUFFER_BIND_TYPE, BUFFER_USAGE, size_t) override;
void Destroy_Locked(buffer_handle_t) override;
void* BeginRead_Locked(buffer_handle_t handle) override;
void* BeginWrite_Locked(buffer_handle_t handle) override;
void EndReadWrite_Locked(buffer_handle_t handle) override;
bool UpdateBuffer_Locked(buffer_handle_t handle, const void*, size_t) override;
size_t Size_Locked(buffer_handle_t) override;
public:
CDeviceBufferManager();
~CDeviceBufferManager();
////////////////////////////////////////////////////////////////////////////////////////
// Initialization and destruction and high level update funcationality
bool Init();
void Update(uint32 frameId, bool called_during_loading);
void ReleaseEmptyBanks(uint32 frameId);
void Sync(uint32 frameId);
bool Shutdown();
AzRHI::ConstantBuffer* CreateConstantBuffer(
const char* name,
AZ::u32 size,
AzRHI::ConstantBufferUsage usage,
AzRHI::ConstantBufferFlags flags = AzRHI::ConstantBufferFlags::None);
////////////////////////////////////////////////////////////////////////////////////////
// Descriptor blocks
SDescriptorBlock* CreateDescriptorBlock(size_t size);
void ReleaseDescriptorBlock(SDescriptorBlock* pBlock);
////////////////////////////////////////////////////////////////////////////////////////
// Locks the global devicebuffer lock
void LockDevMan() override;
void UnlockDevMan() override;
// Returns the size in bytes of the allocation
size_t Size(buffer_handle_t);
////////////////////////////////////////////////////////////////////////////////////////
// Buffer Resource creation methods
//
buffer_handle_t Create(BUFFER_BIND_TYPE, BUFFER_USAGE, size_t);
void Destroy(buffer_handle_t);
////////////////////////////////////////////////////////////////////////////////////////
// Manual IO operations
//
// Note: it's an error to NOT end an IO operation with EndReadWrite!!!
//
// Note: If you are writing (updating) a buffer only partially, please be aware that the
// the contents of the untouched areas might be undefined as a copy-on-write semantic
// ensures that the updating of buffers does not synchronize with the GPU at any cost.
//
void* BeginRead(buffer_handle_t handle);
void* BeginWrite(buffer_handle_t handle);
void EndReadWrite(buffer_handle_t handle);
bool UpdateBuffer(buffer_handle_t handle, const void*, size_t);
////////////////////////////////////////////////////////////////////////////////////////
// Get Stats back from the devbuffer
bool GetStats(BUFFER_BIND_TYPE, BUFFER_USAGE, SDeviceBufferPoolStats&);
# ifndef NULL_RENDERER
D3DBuffer* GetD3D(buffer_handle_t handle, size_t* outOffset);
# endif
/////////////////////////////////////////////////////////////
// Legacy interface
//
// Use with care, can be removed at any point!
CVertexBuffer* CreateVBuffer(size_t, const AZ::Vertex::Format&, const char*, BUFFER_USAGE usage = BU_STATIC); //waltont given that this is a legacy interface, does it make more sense to just remove it? What is the alternative, new interface? The only place that uses it is BreakableGlass.
void ReleaseVBuffer(CVertexBuffer*);
CIndexBuffer* CreateIBuffer(size_t, const char*, BUFFER_USAGE usage = BU_STATIC);
void ReleaseIBuffer(CIndexBuffer*);
bool UpdateVBuffer(CVertexBuffer*, void*, size_t);
bool UpdateIBuffer(CIndexBuffer*, void*, size_t);
};
class CGuardedDeviceBufferManager
: public NoCopy
{
private:
IDeviceBufferManager* m_pDevMan;
public:
explicit CGuardedDeviceBufferManager(IDeviceBufferManager* pDevMan)
: m_pDevMan(pDevMan)
{
m_pDevMan->LockDevMan();
}
~CGuardedDeviceBufferManager()
{
m_pDevMan->UnlockDevMan();
}
inline buffer_handle_t Create(BUFFER_BIND_TYPE type, BUFFER_USAGE usage, size_t size)
{
return m_pDevMan->Create_Locked(type, usage, size);
}
inline void Destroy(buffer_handle_t handle)
{
return m_pDevMan->Destroy_Locked(handle);
}
inline void* BeginRead(buffer_handle_t handle)
{
return m_pDevMan->BeginRead_Locked(handle);
}
inline void* BeginWrite(buffer_handle_t handle)
{
return m_pDevMan->BeginWrite_Locked(handle);
}
inline void EndReadWrite(buffer_handle_t handle)
{
m_pDevMan->EndReadWrite_Locked(handle);
}
inline bool UpdateBuffer(buffer_handle_t handle, const void* src, size_t size)
{
return m_pDevMan->UpdateBuffer_Locked(handle, src, size);
}
# ifndef NULL_RENDERER
inline D3DBuffer* GetD3D(buffer_handle_t handle, size_t* offset)
{
return m_pDevMan->GetD3D(handle, offset);
}
# endif
};
class SRecursiveSpinLock
{
volatile LONG m_lock;
volatile threadID m_owner;
volatile uint16 m_counter;
enum
{
SPIN_COUNT = 10
};
public:
SRecursiveSpinLock()
: m_lock()
, m_owner()
, m_counter()
{}
~SRecursiveSpinLock() {}
void Lock()
{
threadID threadId = CryGetCurrentThreadId();
int32 iterations = 0;
retry:
IF (CryInterlockedCompareExchange(&(this->m_lock), 1L, 0L) == 0L, 1)
{
assert (m_owner == 0u && m_counter == 0u);
m_owner = threadId;
m_counter = 1;
}
else
{
IF (m_owner == threadId, 1)
{
++m_counter;
}
else
{
Sleep((1 & isneg(SPIN_COUNT - iterations++)));
goto retry;
}
}
}
bool TryLock()
{
threadID threadId = CryGetCurrentThreadId();
IF (CryInterlockedCompareExchange(&m_lock, 1L, 0L) == 0L, 1)
{
assert (m_owner == 0u && m_counter == 0u);
m_owner = threadId;
m_counter = 1;
return true;
}
else
{
IF (m_owner == threadId, 1)
{
++m_counter;
return true;
}
else
{
return false;
}
}
}
void Unlock()
{
assert (m_owner == CryGetCurrentThreadId() && m_counter != 0u);
IF ((m_counter -= 1) == 0u, 1)
{
m_owner = 0u;
m_lock = 0L;
MemoryBarrier();
}
}
};
class SRecursiveSpinLocker
{
SRecursiveSpinLock* lock;
public:
SRecursiveSpinLocker(SRecursiveSpinLock* _lock)
: lock(_lock)
{
lock->Lock();
}
~SRecursiveSpinLocker() { lock->Unlock(); }
};
#define SREC_AUTO_LOCK(x) SRecursiveSpinLocker AZ_JOIN(_lock, __LINE__)(&(x))
class CConditonalDevManLock
{
CDeviceBufferManager* m_pDevBufMan;
int m_Active;
public:
explicit CConditonalDevManLock(CDeviceBufferManager* DevMan, int active)
: m_pDevBufMan(DevMan)
, m_Active(active)
{
if (m_Active)
{
m_pDevBufMan->LockDevMan();
}
}
~CConditonalDevManLock()
{
if (m_Active)
{
m_pDevBufMan->UnlockDevMan();
}
}
};
// WrappedDX11Buffer Flags
#define DX11BUF_DYNAMIC BIT(0)
#define DX11BUF_STRUCTURED BIT(1)
#define DX11BUF_BIND_SRV BIT(2)
#define DX11BUF_BIND_UAV BIT(3)
#define DX11BUF_UAV_APPEND BIT(4)
#define DX11BUF_DRAWINDIRECT BIT(5)
#define DX11BUF_STAGING BIT(6)
#if !defined(NULL_RENDERER)
struct WrappedDX11Buffer
{
WrappedDX11Buffer()
: m_pBuffer{}
, m_pSRV{}
, m_pUAV{}
, m_numElements{}
, m_elementSize{}
, m_elementFormat{DXGI_FORMAT_UNKNOWN}
, m_flags(0)
, m_currentBuffer{}
{
}
~WrappedDX11Buffer();
WrappedDX11Buffer(const WrappedDX11Buffer& src);
WrappedDX11Buffer& operator=(const WrappedDX11Buffer& rhs);
bool operator==(const WrappedDX11Buffer& other) const;
D3DUnorderedAccessView* GetUnorderedAccessView() const
{
return m_pUAV[m_currentBuffer];
}
D3DShaderResourceView* GetShaderResourceView() const
{
return m_pSRV[m_currentBuffer];
}
void Create(uint32 numElements, uint32 elementSize, DXGI_FORMAT elementFormat, uint32 flags, const void* pData, int32 nESRAMOffset = -1);
void Release();
void UpdateBufferContent(void* pData, size_t nSize);
static const uint32_t MAX_VIEW_COUNT = 3;
D3DBuffer* m_pBuffer;
D3DShaderResourceView* m_pSRV[MAX_VIEW_COUNT];
D3DUnorderedAccessView* m_pUAV[MAX_VIEW_COUNT];
uint32 m_elementSize;
uint32 m_numElements;
DXGI_FORMAT m_elementFormat;
uint32 m_flags;
uint32_t m_currentBuffer;
};
#endif