Files
UnrealEngine/Engine/Source/Developer/Horde/Public/Compute/ComputeBuffer.h
2025-05-18 13:04:45 +08:00

180 lines
5.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include <memory>
struct FHeader;
struct FComputeBufferDetail;
class FComputeBufferReader;
class FComputeBufferWriter;
//
// Implements a ring buffer using shared memory, with one writer and multiple readers.
//
// Contention between the writer and readers is resolved by splitting the buffer into a number of
// fixed-size chunks, which can be appended to and read immediately, but only reset once consumed
// by all readers.
//
// If the read position is never advanced and only one chunk is used, the buffer functions as an
// append-only buffer.
//
class FComputeBuffer
{
public:
// Maximum allowed number of readers
static const int MaxReaders = 16;
// Maximum allowed number of chunks
static const int MaxChunks = 16;
// Maximum length of a compute buffer name
static const size_t MaxNameLength = 256;
// Parameters for constructing a compute buffer
struct FParams
{
const char* Name = nullptr; // When creating a buffer, we should not normally set the name. We can only *OPEN* buffers with a name. Usually we create buffers and attach them to channels.
int NumChunks = 2;
int ChunkLength = 512 * 1024;
int NumReaders = 1;
bool Anonymous = false; // Set this to true to use anonymous buffers. This could be used together with for example socket transports
};
HORDE_API FComputeBuffer();
HORDE_API FComputeBuffer(const FComputeBuffer& Other);
HORDE_API FComputeBuffer(FComputeBuffer&& Other) noexcept;
HORDE_API ~FComputeBuffer();
HORDE_API FComputeBuffer& operator=(const FComputeBuffer& Other);
HORDE_API FComputeBuffer& operator=(FComputeBuffer&& Other) noexcept;
// Creates a new buffer
HORDE_API bool CreateNew(const FParams& Params);
// Opens an existing shared memory buffer (typically from handles created in another process)
HORDE_API bool OpenExisting(const char* Name);
// Close the current buffer and release all allocated resources
HORDE_API void Close();
// Test if the buffer is currently open
HORDE_API bool IsValid() const { return Detail != nullptr; }
// Creates a new reader for this buffer
HORDE_API FComputeBufferReader CreateReader();
// Creates a new writer for this buffer
HORDE_API FComputeBufferWriter CreateWriter();
private:
friend class FWorkerComputeSocket;
FComputeBufferDetail* Detail;
const char* GetName() const;
};
//
// Facilitates reading data from a compute buffer
//
class FComputeBufferReader
{
public:
HORDE_API FComputeBufferReader();
HORDE_API FComputeBufferReader(const FComputeBufferReader& Other);
HORDE_API FComputeBufferReader(FComputeBufferReader&& Other) noexcept;
HORDE_API ~FComputeBufferReader();
HORDE_API FComputeBufferReader& operator=(const FComputeBufferReader& Other);
HORDE_API FComputeBufferReader& operator=(FComputeBufferReader&& Other) noexcept;
// Closes the handle to the underlying reader instance, resetting this instance back to empty
HORDE_API void Close();
// Detaches this reader from the buffer, causing all pending and subsequent reads to return immediately.
HORDE_API void Detach();
// Test if the reader is valid
bool IsValid() const { return Detail != nullptr; }
// Test whether the buffer has finished being written to (ie. MarkComplete() has been called by the writer) and all data has been read from it.
HORDE_API bool IsComplete() const;
// Move the read cursor forwards by the given number of bytes
HORDE_API void AdvanceReadPosition(size_t Size);
// Gets the amount of data that is ready to be read from a contiguous block of memory.
HORDE_API size_t GetMaxReadSize() const;
// Reads data into the given buffer
HORDE_API size_t Read(void* Buffer, size_t MaxSize, int TimeoutMs = -1, bool* bOutTimedOut = nullptr);
// Waits until the given amount of data has been read, and returns a pointer to it. Returns nullptr if the timeout expires, or
// if the requested amount of data is not in a contiguous block of memory.
HORDE_API const unsigned char* WaitToRead(size_t MinSize, int TimeoutMs = -1, bool* bOutTimedOut = nullptr);
private:
struct FReaderRef;
friend class FComputeBuffer;
friend class FWorkerComputeSocket;
FComputeBufferDetail* Detail;
int ReaderIdx;
FComputeBufferReader(FComputeBufferDetail* Detail, int ReaderIdx);
const char* GetName() const;
};
//
// Facilitates writing data to a compute buffer
//
class FComputeBufferWriter
{
public:
HORDE_API FComputeBufferWriter();
HORDE_API FComputeBufferWriter(const FComputeBufferWriter& Other);
HORDE_API FComputeBufferWriter(FComputeBufferWriter&& Other) noexcept;
HORDE_API ~FComputeBufferWriter();
HORDE_API FComputeBufferWriter& operator=(const FComputeBufferWriter& Other);
HORDE_API FComputeBufferWriter& operator=(FComputeBufferWriter&& Other) noexcept;
// Closes the handle to the underlying writer instance, resetting this instance back to empty
HORDE_API void Close();
// Test if the writer is valid
bool IsValid() const { return Detail != nullptr; }
// Signal that we've finished writing to this buffer
HORDE_API void MarkComplete();
// Move the write cursor forward by the given number of bytes
HORDE_API void AdvanceWritePosition(size_t Size);
// Gets the length of the current write buffer.
HORDE_API size_t GetMaxWriteSize() const;
// Get the max length of a chunk.
HORDE_API size_t GetChunkMaxLength() const;
// Writes data into the compute buffer
HORDE_API size_t Write(const void* Buffer, size_t MaxSize, int TimeoutMs = -1);
// Waits until a write buffer of the requested size is available, and returns a pointer to it. Returns nullptr if the
// timeout expires before enough data has been flushed to return a buffer of the given length.
HORDE_API unsigned char* WaitToWrite(size_t MinSize, int TimeoutMs = -1);
private:
friend class FComputeBuffer;
friend class FWorkerComputeSocket;
FComputeBufferDetail* Detail;
FComputeBufferWriter(FComputeBufferDetail* Detail);
const char* GetName() const;
};