#pragma once #include #include #include #include class DeviceManager; class RenderGraph; struct FrameResources; // VMA-backed allocator + upload helper. // Creates buffers/images, offers an immediate-submit path, and supports // deferring uploads into a single Render Graph transfer pass per frame. class ResourceManager { public: struct MipLevelCopy { uint64_t offset{0}; uint64_t length{0}; uint32_t width{0}; uint32_t height{0}; }; struct BufferCopyRegion { VkBuffer destination = VK_NULL_HANDLE; VkDeviceSize dstOffset = 0; VkDeviceSize size = 0; VkDeviceSize stagingOffset = 0; }; struct PendingBufferUpload { AllocatedBuffer staging; std::vector copies; }; struct PendingImageUpload { AllocatedBuffer staging; VkImage image = VK_NULL_HANDLE; VkExtent3D extent{0, 0, 0}; VkFormat format = VK_FORMAT_UNDEFINED; VkImageLayout initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; VkImageLayout finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; bool generateMips = false; uint32_t mipLevels = 1; // For multi-region (per-mip) uploads std::vector copies; }; void init(DeviceManager *deviceManager); void cleanup(); AllocatedBuffer create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage) const; void destroy_buffer(const AllocatedBuffer &buffer) const; AllocatedImage create_image(VkExtent3D size, VkFormat format, VkImageUsageFlags usage, bool mipmapped = false) const; // Variant with explicit mip level count (>=1). If 0, computes full chain when mipmapped. AllocatedImage create_image(VkExtent3D size, VkFormat format, VkImageUsageFlags usage, bool mipmapped, uint32_t mipLevelsOverride) const; AllocatedImage create_image(const void *data, VkExtent3D size, VkFormat format, VkImageUsageFlags usage, bool mipmapped = false); // Variant with explicit mip level count used for generate_mipmaps. AllocatedImage create_image(const void *data, VkExtent3D size, VkFormat format, VkImageUsageFlags usage, bool mipmapped, uint32_t mipLevelsOverride); // Create an image from a compressed payload (e.g., KTX2 pre-transcoded BCn). // 'bytes' backs a single staging buffer; 'levels' provides per-mip copy regions. // No GPU mip generation is performed; the number of mips equals levels.size(). AllocatedImage create_image_compressed(const void* bytes, size_t size, VkFormat fmt, std::span levels, VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT); // Create a layered image (2D array or cubemap) from a compressed payload. // - 'bytes' is the full KTX2 data payload staged into one buffer // - 'regions' lists VkBufferImageCopy entries (one per mip × layer) // - 'mipLevels' and 'layerCount' define the image subresource counts // - for cubemaps, pass flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT and layerCount=6 AllocatedImage create_image_compressed_layers(const void* bytes, size_t size, VkFormat fmt, uint32_t mipLevels, uint32_t layerCount, std::span regions, VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT, VkImageCreateFlags flags = 0); void destroy_image(const AllocatedImage &img) const; GPUMeshBuffers uploadMesh(std::span indices, std::span vertices); void immediate_submit(std::function &&function) const; bool has_pending_uploads() const; void clear_pending_uploads(); void process_queued_uploads_immediate(); void register_upload_pass(RenderGraph &graph, FrameResources &frame); void set_deferred_uploads(bool enabled) { _deferUploads = enabled; } bool deferred_uploads() const { return _deferUploads; } private: DeviceManager *_deviceManager = nullptr; // immediate submit structures VkFence _immFence = nullptr; VkCommandBuffer _immCommandBuffer = nullptr; VkCommandPool _immCommandPool = nullptr; std::vector _pendingBufferUploads; std::vector _pendingImageUploads; bool _deferUploads = false; DeletionQueue _deletionQueue; mutable std::mutex _pendingMutex; };