Files
QuaternionEngine/docs/EngineContext.md

91 lines
4.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Engine Context: Access to Managers + PerFrame 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 growable `DescriptorAllocatorGrowable` used across modules.
- Global managers: Non-owning pointers to `SwapchainManager`, `DescriptorManager` (prebuilt layouts), `SamplerManager`, and `SceneManager`.
- Per-frame state: `currentFrame` (command buffer, per-frame descriptor pool, deletion queue), `stats`, and `drawExtent`.
- Subsystems: `compute` (`ComputeManager`) and `pipelines` (`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
- Builtin passes no longer call `vkCmdBeginRendering` or 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.md` for the builder API and scheduling.
### Quick Start — In a Render Pass (essentials)
```c++
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: `VulkanEngine` constructs managers, initializes `_context`, then assigns `pipelines`, `compute`, `scene`, etc.
- Per-frame: Engine sets `currentFrame` and `drawExtent`, optionally triggers `PipelineManager::hotReloadChanged()`.
- Cleanup: Managers own their resources; modules should free layouts/sets they create and push per-frame deletions to `currentFrame->_deletionQueue`.
### Best Practices
- Prefer `EngineContext` accessors (`getDevice()`, `getResources()`, `getSwapchain()`) for clarity and testability.
- Re-fetch pipeline/layout by key every frame if using hot reload.
- Use `currentFrame->_frameDescriptors` for transient sets; use `context->descriptors` for longer-lived sets.
- Push resource cleanup to the frame or pass deletion queues to match lifetime with usage.