91 lines
4.6 KiB
Markdown
91 lines
4.6 KiB
Markdown
## 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 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
|
||
|
||
- Built‑in 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.
|
||
|