4.6 KiB
4.6 KiB
Engine Context: Access to Managers + Per‑Frame State
Central DI-style handle that modules use to access device/managers, per-frame state, and convenience data without depending directly on VulkanEngine.
Overview
- Ownership: Holds shared owners for
DeviceManager,ResourceManager, and a growableDescriptorAllocatorGrowableused across modules. - Global managers: Non-owning pointers to
SwapchainManager,DescriptorManager(prebuilt layouts),SamplerManager, andSceneManager. - Per-frame state:
currentFrame(command buffer, per-frame descriptor pool, deletion queue),stats, anddrawExtent. - Subsystems:
compute(ComputeManager) andpipelines(PipelineManager) exposed for unified graphics/compute API. - Window + content:
window(SDL handle) and convenience meshes (cubeMesh,sphereMesh).
Context is wired in VulkanEngine::init() and refreshed each frame before passes execute.
Render Graph Note
- Built‑in passes no longer call
vkCmdBeginRenderingor perform image layout transitions directly. - Use your pass’
register_graph(graph, ...)to declare attachments and resource accesses; the Render Graph inserts barriers and begins/ends dynamic rendering. - See
docs/RenderGraph.mdfor the builder API and scheduling.
Quick Start — In a Render Pass (essentials)
void MyPass::init(EngineContext* context) {
_context = context;
// Use common descriptor layouts provided by DescriptorManager
VkDescriptorSetLayout sceneLayout = _context->getDescriptorLayouts()->gpuSceneDataLayout();
// Build a pipeline via PipelineManager (re-fetch on draw for hot reload)
GraphicsPipelineCreateInfo info{};
info.vertexShaderPath = "../shaders/fullscreen.vert.spv";
info.fragmentShaderPath = "../shaders/my_pass.frag.spv";
info.setLayouts = { sceneLayout };
info.configure = [this](PipelineBuilder& b){
b.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
b.set_polygon_mode(VK_POLYGON_MODE_FILL);
b.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE);
b.set_multisampling_none();
b.disable_depthtest();
b.set_color_attachment_format(_context->getSwapchain()->drawImage().imageFormat);
};
_context->pipelines->createGraphicsPipeline("my_pass", info);
}
void MyPass::execute(VkCommandBuffer cmd) {
// Fetch latest pipeline in case of hot reload
VkPipeline p{}; VkPipelineLayout l{};
_context->pipelines->getGraphics("my_pass", p, l);
// Per-frame uniform buffer via currentFrame allocator
AllocatedBuffer ubuf = _context->getResources()->create_buffer(
sizeof(GPUSceneData), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU);
_context->currentFrame->_deletionQueue.push_function([=, this]{ _context->getResources()->destroy_buffer(ubuf); });
VmaAllocationInfo ai{}; vmaGetAllocationInfo(_context->getDevice()->allocator(), ubuf.allocation, &ai);
*static_cast<GPUSceneData*>(ai.pMappedData) = _context->getSceneData();
VkDescriptorSet set = _context->currentFrame->_frameDescriptors.allocate(
_context->getDevice()->device(), _context->getDescriptorLayouts()->gpuSceneDataLayout());
DescriptorWriter writer;
writer.write_buffer(0, ubuf.buffer, sizeof(GPUSceneData), 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
writer.update_set(_context->getDevice()->device(), set);
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, p);
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, l, 0, 1, &set, 0, nullptr);
// Viewport/scissor from context draw extent
VkViewport vp{0,0,(float)_context->getDrawExtent().width,(float)_context->getDrawExtent().height,0.f,1.f};
vkCmdSetViewport(cmd, 0, 1, &vp);
VkRect2D sc{{0,0},{_context->getDrawExtent().width,_context->getDrawExtent().height}};
vkCmdSetScissor(cmd, 0, 1, &sc);
vkCmdDraw(cmd, 3, 1, 0, 0);
}
Life Cycle
- Init:
VulkanEngineconstructs managers, initializes_context, then assignspipelines,compute,scene, etc. - Per-frame: Engine sets
currentFrameanddrawExtent, optionally triggersPipelineManager::hotReloadChanged(). - Cleanup: Managers own their resources; modules should free layouts/sets they create and push per-frame deletions to
currentFrame->_deletionQueue.
Best Practices
- Prefer
EngineContextaccessors (getDevice(),getResources(),getSwapchain()) for clarity and testability. - Re-fetch pipeline/layout by key every frame if using hot reload.
- Use
currentFrame->_frameDescriptorsfor transient sets; usecontext->descriptorsfor longer-lived sets. - Push resource cleanup to the frame or pass deletion queues to match lifetime with usage.