Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
12 changes: 11 additions & 1 deletion EvoEngine_SDK/include/Core/AssetManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,16 @@ class IAsset;
*/
class AssetManager {
public:
enum class AssetLoadState { Discovered, Queued, LoadingCpu, WaitingForFinalize, Loaded, Failed, Cancelled };
enum class AssetLoadState {
Discovered,
Queued,
LoadingCpu,
WaitingForFinalize,
GpuPending,
Loaded,
Failed,
Cancelled
};

struct AssetLoadSnapshot {
size_t total = 0;
Expand All @@ -32,6 +41,7 @@ class AssetManager {
size_t queued = 0;
size_t loading_cpu = 0;
size_t waiting_for_finalize = 0;
size_t gpu_pending = 0;
Handle active_asset_handle = 0;
AssetLoadState active_state = AssetLoadState::Discovered;
std::string active_asset_name;
Expand Down
26 changes: 26 additions & 0 deletions EvoEngine_SDK/include/Core/ECS/IAsset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
#pragma once
#include "IHandle.hpp"
#include "ISerializable.hpp"
#include "Jobs.hpp"

#include <memory>
#include <mutex>
#include <vector>

namespace evo_engine {

Expand Down Expand Up @@ -29,8 +34,14 @@ class StagedAssetLoadPayload {
* deserialization, and interactions with the asset's file system and the editor.
*/
class IAsset : public ISerializable {
struct PendingGpuWorkState {
mutable std::mutex mutex;
std::vector<JobHandle> handles;
};

std::weak_ptr<IAsset>
self_; /**< Weak reference to the current IAsset instance. Used internally for managing self-references. */
std::shared_ptr<PendingGpuWorkState> pending_gpu_work_state_ = std::make_shared<PendingGpuWorkState>();

protected:
/** @cond DOXYGEN_SHOULD_SKIP_THIS */
Expand Down Expand Up @@ -87,6 +98,21 @@ class IAsset : public ISerializable {
virtual bool ApplyStagedPayloadInternal(const std::filesystem::path& path,
const std::shared_ptr<StagedAssetLoadPayload>& payload);

/**
* @brief Tracks asynchronous GPU work that must complete before this asset is fully ready.
*/
void TrackPendingGpuWork(const JobHandle& handle);

/**
* @brief Takes tracked GPU work handles for readiness orchestration.
*/
[[nodiscard]] std::vector<JobHandle> ConsumePendingGpuWorkHandles() const;

/**
* @brief Waits for tracked GPU work and clears consumed readiness handles.
*/
void WaitForPendingGpuWork() const;

bool saved_ = false; /**< Indicates whether the asset is in a saved state. */
uint32_t version_ = 0; /**< The version number of the asset. */

Expand Down
6 changes: 6 additions & 0 deletions EvoEngine_SDK/include/Layers/RenderLayer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,12 @@ class RenderLayer final : public ILayer {
* \brief Applies all animators associated with this render layer.
*/
void ApplyAnimators() const;

/**
* \brief Drains GPU work before render-layer resources are released.
*/
void OnDestroy() override;

friend class TextureStorage;
std::vector<std::shared_ptr<DescriptorSet>> per_frame_descriptor_sets_ = {};
std::vector<std::shared_ptr<DescriptorSet>> meshlet_descriptor_sets_ = {};
Expand Down
82 changes: 77 additions & 5 deletions EvoEngine_SDK/include/Rendering/Geometry/GeometryStorage.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "Platform.hpp"
#include "Vertex.hpp"

#include <initializer_list>

namespace evo_engine {

/**
Expand Down Expand Up @@ -125,30 +127,35 @@ class RangeDescriptor {
/**
* @brief Offset value of the range descriptor.
*/
uint32_t offset;
uint32_t offset = 0;

/**
* @brief Range value for the descriptor.
*
* - When used for meshlets: Represents the count of meshlets for this geometry.
* - When used for triangles: Represents the count of triangles, including space for empty fillers.
*/
uint32_t range;
uint32_t range = 0;

/**
* @brief Offset for the previous frame's data.
*/
uint32_t prev_frame_offset;
uint32_t prev_frame_offset = 0;

/**
* @brief Number of indices in the current frame.
*/
uint32_t index_count;
uint32_t index_count = 0;

/**
* @brief Number of indices in the previous frame.
*/
uint32_t prev_frame_index_count;
uint32_t prev_frame_index_count = 0;

/**
* @brief Committed range count that is safe for rendering.
*/
uint32_t prev_frame_range = 0;
};

/**
Expand Down Expand Up @@ -210,6 +217,39 @@ class GeometryStorage final {
static GeometryStorage& GetInstance();

private:
struct RangeCommit {
std::shared_ptr<RangeDescriptor> descriptor;
uint32_t offset = 0;
uint32_t range = 0;
uint32_t index_count = 0;
};

struct PendingGeometryUpload {
bool active = false;
std::vector<GpuWorkHandle> handles;
std::vector<RangeCommit> meshlet_commits;
std::vector<RangeCommit> index_commits;
};

struct DirtyRange {
bool dirty = false;
size_t begin = 0;
size_t end = 0;

void Mark(size_t range_begin, size_t count);
void MarkTail(size_t range_begin, size_t range_end);
void Clear();
[[nodiscard]] bool Empty() const;
};

struct DirtyBufferUpload {
const std::shared_ptr<Buffer>* buffer = nullptr;
const void* data = nullptr;
size_t element_count = 0;
size_t element_size = 0;
DirtyRange* dirty_range = nullptr;
};

std::vector<VertexDataChunk> vertex_data_chunks_ = {};
std::vector<Meshlet> meshlets_ = {};
std::vector<std::shared_ptr<RangeDescriptor>> meshlet_range_descriptor_;
Expand All @@ -220,6 +260,10 @@ class GeometryStorage final {
std::shared_ptr<Buffer> meshlet_buffer_ = {};
std::shared_ptr<Buffer> triangle_buffer_ = {};
bool require_mesh_data_device_update_ = {};
PendingGeometryUpload pending_mesh_upload_;
DirtyRange mesh_vertex_dirty_range_;
DirtyRange meshlet_dirty_range_;
DirtyRange triangle_dirty_range_;

std::vector<SkinnedVertexDataChunk> skinned_vertex_data_chunks_ = {};
std::vector<SkinnedMeshlet> skinned_meshlets_ = {};
Expand All @@ -231,6 +275,10 @@ class GeometryStorage final {
std::shared_ptr<Buffer> skinned_meshlet_buffer_ = {};
std::shared_ptr<Buffer> skinned_triangle_buffer_ = {};
bool require_skinned_mesh_data_device_update_ = {};
PendingGeometryUpload pending_skinned_mesh_upload_;
DirtyRange skinned_vertex_dirty_range_;
DirtyRange skinned_meshlet_dirty_range_;
DirtyRange skinned_triangle_dirty_range_;

std::vector<StrandPointDataChunk> strand_point_data_chunks_ = {};
std::vector<StrandMeshlet> strand_meshlets_ = {};
Expand All @@ -242,8 +290,30 @@ class GeometryStorage final {
std::shared_ptr<Buffer> strand_meshlet_buffer_ = {};
std::shared_ptr<Buffer> segment_buffer_ = {};
bool require_strand_mesh_data_device_update_ = {};
PendingGeometryUpload pending_strand_upload_;
DirtyRange strand_point_dirty_range_;
DirtyRange strand_meshlet_dirty_range_;
DirtyRange segment_dirty_range_;

void UploadData();
void ClearMeshDirtyRanges();
void ClearSkinnedMeshDirtyRanges();
void ClearStrandDirtyRanges();
static GpuWorkHandle ScheduleDirtyBufferUpload(const DirtyBufferUpload& upload);
static void CaptureRangeCommits(const std::vector<std::shared_ptr<RangeDescriptor>>& descriptors,
std::vector<RangeCommit>& commits);
static void ApplyRangeCommits(const std::vector<RangeCommit>& commits);
static bool HasValidUploadHandle(const PendingGeometryUpload& upload);
static bool IsPendingUploadCompleted(const PendingGeometryUpload& upload);
static void WaitPendingUpload(PendingGeometryUpload& upload);
static void ClearPendingUpload(PendingGeometryUpload& upload);
bool CompletePendingUpload(PendingGeometryUpload& upload);
void CompletePendingUploads();
void ScheduleUploadGroup(bool& dirty, PendingGeometryUpload& pending_upload,
std::initializer_list<DirtyBufferUpload> uploads,
const std::vector<std::shared_ptr<RangeDescriptor>>& meshlet_descriptors,
const std::vector<std::shared_ptr<RangeDescriptor>>& index_descriptors);
void SchedulePendingUploads();
friend class RenderLayer;
friend class Resources;
friend class Platform;
Expand All @@ -256,6 +326,8 @@ class GeometryStorage final {

public:
[[nodiscard]] static uint32_t GetVersion();
[[nodiscard]] static bool HasPendingUploads();
static void WaitForPendingUploads();
static const std::shared_ptr<Buffer>& GetTriangleBuffer();
static const std::shared_ptr<Buffer>& GetVertexBuffer();
static const std::shared_ptr<Buffer>& GetMeshletBuffer();
Expand Down
25 changes: 24 additions & 1 deletion EvoEngine_SDK/include/Rendering/Platform/GraphicsResources.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,8 @@ class Buffer final : public IGraphicsResource {
const VmaAllocationCreateInfo& vma_allocation_create_info);
static void ResizeOnGpuThread(const std::shared_ptr<GpuState>& state, VkDeviceSize new_size);
static void DestroyOnGpuThread(const std::shared_ptr<GpuState>& state);
static void UploadDataOnGpuThread(const std::shared_ptr<GpuState>& state, size_t size, const void* src);
static void UploadDataOnGpuThread(const std::shared_ptr<GpuState>& state, size_t size, const void* src,
VkDeviceSize dst_offset);
static void DownloadDataOnGpuThread(const std::shared_ptr<GpuState>& state, size_t size, void* dst);
static void CopyFromBufferOnGpuThread(const std::shared_ptr<GpuState>& state,
const std::shared_ptr<GpuState>& src_state, VkDeviceSize size,
Expand Down Expand Up @@ -537,13 +538,29 @@ class Buffer final : public IGraphicsResource {
*/
void UploadData(size_t size, const void* src);

/**
* @brief Uploads data into an existing buffer subrange.
* @param size Size of the data to upload.
* @param src Pointer to the data source.
* @param dst_offset Destination byte offset.
*/
void UploadSubData(size_t size, const void* src, VkDeviceSize dst_offset);

/**
* @brief Enqueues an asynchronous upload to the buffer.
* @param size Size of the data to upload.
* @param src Pointer to the data source. The data is copied before this function returns.
*/
[[nodiscard]] GpuWorkHandle UploadDataAsync(size_t size, const void* src);

/**
* @brief Enqueues an asynchronous upload into an existing buffer subrange.
* @param size Size of the data to upload.
* @param src Pointer to the data source. The data is copied before this function returns.
* @param dst_offset Destination byte offset.
*/
[[nodiscard]] GpuWorkHandle UploadSubDataAsync(size_t size, const void* src, VkDeviceSize dst_offset);

/**
* @brief Downloads data from the buffer.
* @param size Size of the data to download.
Expand Down Expand Up @@ -660,6 +677,12 @@ class Buffer final : public IGraphicsResource {
*/
[[nodiscard]] const VkBuffer& GetVkBuffer() const;

/**
* @brief Retrieves the current allocated buffer size in bytes.
* @return Current buffer size in bytes.
*/
[[nodiscard]] VkDeviceSize GetSize() const;

/**
* @brief Retrieves the VMA allocation handle for the buffer.
* @return VMA allocation handle.
Expand Down
5 changes: 5 additions & 0 deletions EvoEngine_SDK/include/Rendering/Platform/Platform.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,11 @@ class Platform final {
*/
static void WaitForDeviceIdle();

/**
* @brief Drains pending resource upload work and waits for GPU/device idle.
*/
static void DrainGpuResourceWork();

/// List of primitive counts for debugging purposes.
std::vector<size_t> prim_count{};

Expand Down
33 changes: 21 additions & 12 deletions EvoEngine_SDK/include/Rendering/Texture/TextureStorage.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#pragma once
#include "GraphicsResources.hpp"

#include <atomic>

namespace evo_engine {

/**
Expand All @@ -18,6 +20,7 @@ struct TextureStorageHandle {
*/
class Texture2DStorage {
friend class TextureStorage;
friend class Texture2D;
friend class Cubemap;

/**
Expand All @@ -31,11 +34,9 @@ class Texture2DStorage {
glm::uvec2 new_resolution_{};

/**
* @brief Uploads texture data to the GPU.
* @param data The pixel data to upload.
* @param resolution The resolution of the texture.
* @brief Immediately uploads any pending data to the GPU.
*/
void UploadData(const std::vector<glm::vec4>& data, const glm::uvec2& resolution);
void UploadPendingDataImmediately();

public:
bool pending_delete = false; ///< Indicates whether the storage is pending deletion.
Expand All @@ -47,6 +48,8 @@ class Texture2DStorage {
std::shared_ptr<Sampler> sampler = {}; ///< GPU sampler resource.

ImTextureID im_texture_id = 0; ///< ImGui texture ID for rendering.
std::shared_ptr<std::atomic_size_t> gpu_upload_in_flight =
std::make_shared<std::atomic_size_t>(0); ///< Async GPU uploads currently mutating image state.

/**
* @brief Retrieves the Vulkan image layout of the texture.
Expand Down Expand Up @@ -78,18 +81,24 @@ class Texture2DStorage {
*/
[[nodiscard]] std::shared_ptr<Image> GetImage() const;

/**
* @brief Returns whether an asynchronous GPU upload is still pending.
*/
[[nodiscard]] bool IsGpuUploadPending() const;

/**
* @brief Initializes the GPU resources for the texture with the given resolution.
* @param resolution The resolution of the texture.
*/
void Initialize(const glm::uvec2& resolution);

/**
* @brief Sets texture data and uploads it immediately to the GPU.
* @param data The pixel data to set.
* @brief Sets texture data and uploads it asynchronously through the GPU service.
* @param data The pixel data to upload.
* @param resolution The resolution of the texture.
* @return A handle that completes when the GPU upload finishes.
*/
void SetDataImmediately(const std::vector<glm::vec4>& data, const glm::uvec2& resolution);
[[nodiscard]] GpuWorkHandle SetDataAsync(const std::vector<glm::vec4>& data, const glm::uvec2& resolution);

/**
* @brief Sets texture data and queues it for upload during a batch process.
Expand All @@ -98,11 +107,6 @@ class Texture2DStorage {
*/
void SetData(const std::vector<glm::vec4>& data, const glm::uvec2& resolution);

/**
* @brief Immediately uploads any pending data to the GPU.
*/
void UploadDataImmediately();

/**
* @brief Clears the texture resources and data.
*/
Expand Down Expand Up @@ -202,6 +206,11 @@ class TextureStorage final {
*/
[[nodiscard]] static uint32_t GetVersion();

/**
* @brief Returns whether any texture storage still has queued or in-flight GPU upload work.
*/
[[nodiscard]] static bool HasPendingUploads();

/**
* @brief Synchronizes the device to ensure all texture-related operations are complete.
*/
Expand Down
Loading
Loading