feat: add forward+ rendering

This commit is contained in:
2026-10-11 00:12:23 +02:00
parent c96b2282fb
commit 4bf87bd91b
26 changed files with 1346 additions and 191 deletions
+97 -14
View File
@@ -21,7 +21,8 @@ GameRenderer::GameRenderer()
void GameRenderer::init(GfxDevice& gfxDevice, RenderResources& _resources, glm::ivec2 drawImageSize)
{
try {
try
{
resources = &_resources;
sceneDataBuffer.init(
gfxDevice,
@@ -34,12 +35,15 @@ void GameRenderer::init(GfxDevice& gfxDevice, RenderResources& _resources, glm::
meshPipeline = std::make_unique<MeshPipeline>();
meshPipeline->init(gfxDevice, _resources, drawImageFormat, depthImageFormat);
skyboxPipeline = std::make_unique<SkyboxPipeline>();
skyboxPipeline = std::make_unique<SkyboxPipeline>();
skyboxPipeline->init(gfxDevice, _resources, drawImageFormat, depthImageFormat);
skinningPipeline = std::make_unique<SkinningPipeline>();
skinningPipeline->init(gfxDevice);
lightCullingPipeline = std::make_unique<LightCullingPipeline>();
lightCullingPipeline->init(gfxDevice, _resources, drawImageSize);
lineRenderingPass = std::make_unique<LineRenderingPass>();
lineRenderingPass->init(gfxDevice, drawImageFormat);
@@ -48,7 +52,9 @@ skyboxPipeline = std::make_unique<SkyboxPipeline>();
GameState::GetInstance().SetRenderer(this);
initialized = true;
} catch (...) {
}
catch (...)
{
cleanup(gfxDevice);
throw;
}
@@ -59,6 +65,7 @@ void GameRenderer::beginDrawing(GfxDevice& gfxDevice)
flushMaterialUpdates(gfxDevice);
meshDrawCommands.clear();
skinningPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex());
lightCullingPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex());
QuadRenderingManager::GetInstance().ClearQuads();
}
@@ -70,13 +77,16 @@ void GameRenderer::endDrawing()
std::stable_sort(
sortedMeshDrawCommands.begin(),
sortedMeshDrawCommands.end(),
[this](std::size_t left, std::size_t right) {
[this](std::size_t left, std::size_t right)
{
const auto& lhs = meshDrawCommands[left];
const auto& rhs = meshDrawCommands[right];
if (lhs.meshId != rhs.meshId) {
if (lhs.meshId != rhs.meshId)
{
return lhs.meshId < rhs.meshId;
}
if (lhs.materialId != rhs.materialId) {
if (lhs.materialId != rhs.materialId)
{
return lhs.materialId < rhs.materialId;
}
return (lhs.skinnedMesh != nullptr) < (rhs.skinnedMesh != nullptr);
@@ -106,9 +116,17 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
}
}
{
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Lights Buffer Flush");
lightCullingPipeline->flushCurrentFrame(cmd, gfxDevice);
}
// TODO: there has to be a better way to do this
const auto gpuSceneData = GPUSceneData{
.view = sceneData.camera.GetViewMatrix(),
.proj = sceneData.camera.GetProjectionMatrix(),
.invProj = glm::inverse(sceneData.camera.GetProjectionMatrix()),
.viewProj = sceneData.camera.GetViewProjectionMatrix(),
.cameraPos = glm::vec4{sceneData.camera.m_position, 1.f},
.ambientColor = {sceneData.ambientColor},
@@ -116,6 +134,12 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
.fogColor = {sceneData.fogColor},
.fogDensity = sceneData.fogDensity,
.materialsBuffer = resources->materials().getMaterialDataBufferAddress(),
.lightsBuffer = lightCullingPipeline->getLightsBufferAddress(),
.tileLightIndicesBuffer = lightCullingPipeline->getTileLightIndicesBufferAddress(),
.numLights = lightCullingPipeline->getNumLights(),
.tileCountX = lightCullingPipeline->getTileCount().x,
.debugLightHeatmap = renderLightHeatmap ? 1u : 0u,
.padding = 0,
};
{
@@ -165,12 +189,60 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
depthImage.setLayout(VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL);
}
{
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Depth Prepass");
auto depthPrepassInfo = vkutil::createRenderingInfo({
.renderExtent = drawImage.getExtent2D(),
.depthImageView = depthImage.imageView,
.depthImageClearValue = 1.f,
});
depthPrepassInfo.renderingInfo.pDepthAttachment = &depthPrepassInfo.depthAttachment;
vkCmdBeginRendering(cmd, &depthPrepassInfo.renderingInfo);
meshPipeline->drawDepthPrepass(
cmd,
drawImage.getExtent2D(),
*resources,
camera,
sceneDataBuffer.getBuffer(),
meshDrawCommands,
sortedMeshDrawCommands);
vkCmdEndRendering(cmd);
}
{
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Light Culling");
vkutil::transitionImage(
cmd,
depthImage.image,
depthImage.layout,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
depthImage.setLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
lightCullingPipeline->cullLights(
cmd,
*resources,
sceneDataBuffer.getBuffer(),
depthImageId,
drawImage.getExtent2D());
vkutil::transitionImage(
cmd,
depthImage.image,
depthImage.layout,
VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL);
depthImage.setLayout(VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL);
}
auto renderInfo = vkutil::createRenderingInfo({
.renderExtent = drawImage.getExtent2D(),
.colorImageView = drawImage.imageView,
.colorImageClearValue = glm::vec4{0.f, 0.f, 0.f, 1.f},
.depthImageView = depthImage.imageView,
.depthImageClearValue = 1.f,
});
renderInfo.renderingInfo.pColorAttachments = &renderInfo.colorAttachment;
renderInfo.renderingInfo.pDepthAttachment = &renderInfo.depthAttachment;
@@ -226,11 +298,11 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
*resources,
drawImage,
glm::ortho(0.0f,
static_cast<float>(drawImage.extent.width),
static_cast<float>(drawImage.extent.height),
0.0f, -1.0f, 1.0f));
static_cast<float>(drawImage.extent.width),
static_cast<float>(drawImage.extent.height),
0.0f, -1.0f, 1.0f));
}
// vkutil::cmdEndLabel(cmd);
// vkutil::cmdEndLabel(cmd);
}
void GameRenderer::drawQuads(
@@ -246,11 +318,12 @@ void GameRenderer::cleanup(GfxDevice& gfxDevice)
{
VkDevice device = gfxDevice.getDevice();
if (device != VK_NULL_HANDLE) {
if (device != VK_NULL_HANDLE)
{
vkDeviceWaitIdle(device);
}
if (skinningPipeline)
if (skinningPipeline)
skinningPipeline->cleanup(gfxDevice);
if (lineRenderingPass)
@@ -259,6 +332,9 @@ if (skinningPipeline)
if (quadRendererPass)
quadRendererPass->cleanup(gfxDevice);
if (lightCullingPipeline)
lightCullingPipeline->cleanup(gfxDevice);
if (skyboxPipeline)
skyboxPipeline->cleanup(device);
@@ -281,6 +357,7 @@ if (skinningPipeline)
skinningPipeline.reset();
lineRenderingPass.reset();
quadRendererPass.reset();
lightCullingPipeline.reset();
pendingMaterialUploads.clear();
meshDrawCommands.clear();
sortedMeshDrawCommands.clear();
@@ -323,6 +400,11 @@ void GameRenderer::drawSkinnedMesh(MeshID id,
});
}
void GameRenderer::submitLight(const Light& light)
{
lightCullingPipeline->appendLight(light);
}
const GPUImage& GameRenderer::getDrawImage() const
{
assert(resources);
@@ -349,7 +431,8 @@ void GameRenderer::setSkyboxTexture(ImageID skyboxImageId)
void GameRenderer::flushMaterialUpdates(GfxDevice& gfxDevice)
{
if (!pendingMaterialUploads.empty()) {
if (!pendingMaterialUploads.empty())
{
// Material data is currently shared by all frame contexts. Wait before
// mutating it so an older in-flight frame cannot read the overwritten
// range. This can later be replaced with per-frame material buffers.