16#include <SDL2/SDL_vulkan.h>
34#include "common/config.h"
38#include "zeroerr/assert.h"
43#include <assimp/mesh.h>
44#include <assimp/matrix3x3.h>
45#include <assimp/matrix4x4.h>
46#include <assimp/vector3.h>
47#include <glm/gtc/matrix_transform.hpp>
49#include "graphics/shaders/color_vert_spv.inc"
50#include "graphics/shaders/color_frag_spv.inc"
51#include "graphics/shaders/textured_vert_spv.inc"
52#include "graphics/shaders/textured_frag_spv.inc"
53#include "graphics/shaders/mesh3d_vert_spv.inc"
54#include "graphics/shaders/mesh3d_frag_spv.inc"
55#include "graphics/shaders/mesh3d_clustered_vert_spv.inc"
56#include "graphics/shaders/mesh3d_clustered_frag_spv.inc"
57#include "graphics/shaders/mesh3d_shadow_vert_spv.inc"
58#include "graphics/shaders/mesh3d_shadow_frag_spv.inc"
59#include "graphics/shaders/mesh3d_shadow_alpha_vert_spv.inc"
60#include "graphics/shaders/mesh3d_shadow_alpha_frag_spv.inc"
61#include "graphics/shaders/mesh3d_gbuffer_vert_spv.inc"
62#include "graphics/shaders/mesh3d_gbuffer_frag_spv.inc"
63#include "graphics/shaders/mesh3d_gbuffer_alpha_frag_spv.inc"
64#include "graphics/shaders/mesh3d_hair_vert_spv.inc"
65#include "graphics/shaders/mesh3d_hair_frag_spv.inc"
66#include "graphics/shaders/lit2d_vert_spv.inc"
67#include "graphics/shaders/lit2d_frag_spv.inc"
74vk::Pipeline createSolidColorPipeline(vkb::Device &
device,
const vkb::BuiltRenderPass &renderPass,
81 std::vector<vk::PipelineColorBlendAttachmentState> attachments(1,
82 makeBlendAttachment(mode));
83 vk::PipelineColorBlendStateCreateInfo cbs{};
84 cbs.logicOpEnable =
false;
85 cbs.attachmentCount = 1;
86 cbs.pAttachments = attachments.data();
87 return device.createPipeline()
88 .useClassicPipeline(embeddedSpirv(color_vert_spv), embeddedSpirv(color_frag_spv))
90 .setVertexInputState(vkb::VertexInputStateBuilder()
91 .addInputBinding<ColorVertex>()
92 .addAttributeDescription<ColorVertex>())
93 .setDynamicStatesViewportScissor()
94 .setRasterizer(vk::PolygonMode::eFill,
false,
false, 1.0f,
95 vk::CullModeFlagBits::eNone, vk::FrontFace::eCounterClockwise)
96 .setColorBlending(cbs)
100 return device.createPipeline()
101 .useClassicPipeline(embeddedSpirv(color_vert_spv), embeddedSpirv(color_frag_spv))
102 .setPipelineLayout(
layout)
103 .setVertexInputState(vkb::VertexInputStateBuilder()
104 .addInputBinding<ColorVertex>()
105 .addAttributeDescription<ColorVertex>())
106 .setDynamicStatesViewportScissor()
107 .setRasterizer(vk::PolygonMode::eFill,
false,
false, 1.0f,
108 vk::CullModeFlagBits::eNone, vk::FrontFace::eCounterClockwise)
113 return device.createPipeline()
114 .useClassicPipeline(embeddedSpirv(color_vert_spv), embeddedSpirv(color_frag_spv))
115 .setPipelineLayout(
layout)
116 .setVertexInputState(vkb::VertexInputStateBuilder()
117 .addInputBinding<ColorVertex>()
118 .addAttributeDescription<ColorVertex>())
119 .setDynamicStatesViewportScissor()
120 .setRasterizer(vk::PolygonMode::eFill,
false,
false, 1.0f, vk::CullModeFlagBits::eNone,
121 vk::FrontFace::eCounterClockwise)
128void Graphics::createSwapchainAndPipeline() {
131 presentRecording = {};
133 presentModel.destroy();
134 presentModel = vkb::Present{};
136 vkb::SwapchainBuilder swapchainBuilder =
device.createSwapchain();
140 swapchainBuilder.set_desired_format(
141 {vk::Format::eB8G8R8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear});
143 swapchainBuilder.set_desired_present_mode(vk::PresentModeKHR::eMailbox);
144 swapchainBuilder.add_fallback_present_mode(vk::PresentModeKHR::eFifo);
146 swapchainBuilder.set_desired_present_mode(vk::PresentModeKHR::eImmediate);
147 swapchainBuilder.add_fallback_present_mode(vk::PresentModeKHR::eMailbox);
148 swapchainBuilder.add_fallback_present_mode(vk::PresentModeKHR::eFifo);
151 swapchainBuilder.add_image_usage_flags(vk::ImageUsageFlagBits::eTransferSrc);
152 auto swapRet = swapchainBuilder.set_old_swapchain(swapchain).build();
156 depthImage = vkb::DepthStencilImage{
device, swapchain.extent.width, swapchain.extent.height, depthFormat};
159 vkb::RenderPassBuilder rpBuilder{
device};
161 rpBuilder.addPresentAttachment(swapchain.image_format, vk::AttachmentLoadOp::eClear)
162 .addDepthAttachment(depthFormat, vk::AttachmentLoadOp::eClear,
163 vk::AttachmentStoreOp::eDontCare)
164 .addSubpass(vkb::SubpassBuilder()
165 .addAttachmentRef(0, vk::ImageLayout::eColorAttachmentOptimal)
166 .setDepthStencilAttachment(
167 1, vk::ImageLayout::eDepthStencilAttachmentOptimal))
168 .addDependency(VK_SUBPASS_EXTERNAL, 0,
169 vk::PipelineStageFlagBits::eColorAttachmentOutput |
170 vk::PipelineStageFlagBits::eEarlyFragmentTests,
171 vk::PipelineStageFlagBits::eColorAttachmentOutput |
172 vk::PipelineStageFlagBits::eEarlyFragmentTests,
174 vk::AccessFlagBits::eColorAttachmentRead |
175 vk::AccessFlagBits::eColorAttachmentWrite |
176 vk::AccessFlagBits::eDepthStencilAttachmentWrite)
181 pipelineLayout = createPipelineLayout(device);
182 pipeline = createSolidColorPipeline(device, renderpass, pipelineLayout);
183 solidAlphaPipeline = createSolidColorPipeline(device, renderpass, pipelineLayout,
185 additiveSolidPipeline = createSolidColorPipeline(device, renderpass, pipelineLayout,
192 scenePassPipelineTarget = vk::RenderPass(renderpass);
193 scenePassPipelineSamples = vk::SampleCountFlagBits::e1;
195 presentModel =
device.createPresent(swapchain).build(renderpass, depthImage.imageView());
201 presentModel.synchronous_frames =
false;
202 ensurePresentCaptureHook();
203 swapchainDirty =
false;
206void Graphics::createTexturedPipeline() {
207 if (texPipeline)
return;
209 vkb::DescriptorSetLayoutBuilder layoutBuilder;
210 texSetLayoutUnique = layoutBuilder
211 .image(0, vk::DescriptorType::eCombinedImageSampler,
212 vk::ShaderStageFlagBits::eFragment, 1)
213 .image(1, vk::DescriptorType::eCombinedImageSampler,
214 vk::ShaderStageFlagBits::eFragment, 1)
215 .createUnique(
device.instance);
216 texSetLayout = *texSetLayoutUnique;
218 vk::DescriptorPoolSize poolSizes[] = {
219 {vk::DescriptorType::eCombinedImageSampler, 8192},
220 {vk::DescriptorType::eUniformBuffer, 2048},
224 {vk::DescriptorType::eUniformBufferDynamic, 4096},
225 {vk::DescriptorType::eStorageBuffer, 256},
227 vk::DescriptorPoolCreateInfo poolInfo{};
228 poolInfo.maxSets = 4096;
229 poolInfo.poolSizeCount = 4;
230 poolInfo.pPoolSizes = poolSizes;
231 descriptorPool =
device->createDescriptorPool(poolInfo);
233 texPipelineLayout = createPipelineLayout(device, texSetLayout);
235 pushConstantRange(vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
237 shaderPipelineLayout = createPipelineLayout(device, texSetLayout, &pcr);
239 auto vert = embeddedSpirv(textured_vert_spv);
240 auto frag = embeddedSpirv(textured_frag_spv);
241 texPipeline = createTexturedStylePipeline(
vert,
frag, renderpass, texPipelineLayout);
242 additiveTexPipeline = createTexturedStylePipeline(
vert,
frag, renderpass, texPipelineLayout,
244 opaqueTexPipeline = createTexturedStylePipeline(
vert,
frag, renderpass, texPipelineLayout,
247 createLit2DPipeline();
250vk::Pipeline Graphics::createTexturedStylePipeline(
const std::vector<uint32_t> &
vert,
251 const std::vector<uint32_t> &
frag,
252 const vkb::BuiltRenderPass &rp,
254 ShaderModulePair modules(device,
vert,
frag);
258 std::vector<vk::PipelineColorBlendAttachmentState> attachments(1,
259 makeBlendAttachment(mode));
260 vk::PipelineColorBlendStateCreateInfo cbs{};
261 cbs.logicOpEnable =
false;
262 cbs.attachmentCount = 1;
263 cbs.pAttachments = attachments.data();
264 return device.createPipeline()
265 .useClassicPipeline(modules.vert, modules.frag)
266 .setPipelineLayout(
layout)
267 .setVertexInputState(vkb::VertexInputStateBuilder()
268 .addInputBinding<TexturedVertex>()
269 .addAttributeDescription<TexturedVertex>())
270 .setDynamicStatesViewportScissor()
271 .setRasterizer(vk::PolygonMode::eFill,
false,
false, 1.0f,
272 vk::CullModeFlagBits::eNone, vk::FrontFace::eCounterClockwise)
273 .setDepthStencil(
false,
false)
274 .setColorBlending(cbs)
278 return device.createPipeline()
279 .useClassicPipeline(modules.vert, modules.frag)
280 .setPipelineLayout(
layout)
281 .setVertexInputState(vkb::VertexInputStateBuilder()
282 .addInputBinding<TexturedVertex>()
283 .addAttributeDescription<TexturedVertex>())
284 .setDynamicStatesViewportScissor()
285 .setRasterizer(vk::PolygonMode::eFill,
false,
false, 1.0f,
286 vk::CullModeFlagBits::eNone, vk::FrontFace::eCounterClockwise)
287 .setDepthStencil(
false,
false)
292 return device.createPipeline()
293 .useClassicPipeline(modules.vert, modules.frag)
294 .setPipelineLayout(
layout)
295 .setVertexInputState(vkb::VertexInputStateBuilder()
296 .addInputBinding<TexturedVertex>()
297 .addAttributeDescription<TexturedVertex>())
298 .setDynamicStatesViewportScissor()
299 .setRasterizer(vk::PolygonMode::eFill,
false,
false, 1.0f, vk::CullModeFlagBits::eNone,
300 vk::FrontFace::eCounterClockwise)
301 .setDepthStencil(
false,
false)
306void Graphics::createLit2DPipeline() {
307 if (lit2dPipeline)
return;
309 vkb::DescriptorSetLayoutBuilder layoutBuilder;
310 lit2dSetLayoutUnique =
312 .image(0, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment, 1)
313 .image(1, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment, 1)
314 .buffer(2, vk::DescriptorType::eUniformBuffer, vk::ShaderStageFlagBits::eFragment, 1)
315 .createUnique(
device.instance);
316 lit2dSetLayout = *lit2dSetLayoutUnique;
318 lit2dPipelineLayout = createPipelineLayout(device, lit2dSetLayout);
322 offscreenLighting2dUbo.allocate(frameToken(), device, vk::BufferUsageFlagBits::eUniformBuffer,
323 sizeof(Lighting2DUBO),
324 vk::MemoryPropertyFlagBits::eHostVisible |
325 vk::MemoryPropertyFlagBits::eHostCoherent);
327 auto vert = embeddedSpirv(lit2d_vert_spv);
328 auto frag = embeddedSpirv(lit2d_frag_spv);
329 lit2dPipeline = createTexturedStylePipeline(
vert,
frag, renderpass, lit2dPipelineLayout);
332void Graphics::createMesh3DPipeline() {
333 if (mesh3dPipeline)
return;
337 vkb::DescriptorSetLayoutBuilder layoutBuilder;
338 mesh3dSetLayoutUnique =
340 .buffer(0, vk::DescriptorType::eUniformBufferDynamic,
341 vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, 1)
342 .image(1, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment, 1)
343 .image(2, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment, 1)
344 .image(3, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment, 1)
345 .buffer(4, vk::DescriptorType::eUniformBufferDynamic, vk::ShaderStageFlagBits::eFragment, 1)
346 .image(5, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment, 1)
347 .image(6, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment, 1)
348 .image(7, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment, 1)
349 .createUnique(
device.instance);
350 mesh3dSetLayout = *mesh3dSetLayoutUnique;
352 mesh3dPipelineLayout = createPipelineLayout(device, mesh3dSetLayout);
354 pushConstantRange(vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
356 mesh3dShaderPipelineLayout = createPipelineLayout(device, mesh3dSetLayout, &pcr);
360 mesh3dFrameSlots.clear();
362 auto vert = embeddedSpirv(mesh3d_vert_spv);
363 auto frag = embeddedSpirv(mesh3d_frag_spv);
365 createMesh3DStylePipeline(
vert,
frag, mesh3dPipelineLayout, renderpass,
366 vk::SampleCountFlagBits::e1);
369void Graphics::createMesh3DClusteredPipeline() {
370 if (mesh3dClusteredPipeline)
return;
372 vkb::DescriptorSetLayoutBuilder layoutBuilder;
373 mesh3dClusteredSetLayoutUnique =
375 .buffer(0, vk::DescriptorType::eUniformBufferDynamic,
376 vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, 1)
377 .image(1, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment, 1)
378 .image(2, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment, 1)
379 .image(3, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment, 1)
380 .buffer(4, vk::DescriptorType::eStorageBuffer, vk::ShaderStageFlagBits::eFragment, 1)
381 .buffer(5, vk::DescriptorType::eStorageBuffer, vk::ShaderStageFlagBits::eFragment, 1)
382 .buffer(6, vk::DescriptorType::eStorageBuffer, vk::ShaderStageFlagBits::eFragment, 1)
383 .buffer(7, vk::DescriptorType::eUniformBufferDynamic, vk::ShaderStageFlagBits::eFragment, 1)
384 .image(8, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment, 1)
385 .image(9, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment, 1)
386 .createUnique(
device.instance);
387 mesh3dClusteredSetLayout = *mesh3dClusteredSetLayoutUnique;
389 mesh3dClusteredPipelineLayout = createPipelineLayout(device, mesh3dClusteredSetLayout);
391 mesh3dClusteredFrameSlots.clear();
393 auto vert = embeddedSpirv(mesh3d_clustered_vert_spv);
394 auto frag = embeddedSpirv(mesh3d_clustered_frag_spv);
395 mesh3dClusteredPipeline =
396 createMesh3DStylePipeline(
vert,
frag, mesh3dClusteredPipelineLayout, renderpass,
397 vk::SampleCountFlagBits::e1);
400void Graphics::destroyShadowResources() {
401 destroyPipeline(device, shadowPipeline);
402 destroyPipelineLayout(device, shadowPipelineLayout);
403 destroyPipeline(device, shadowAlphaPipeline);
404 destroyPipelineLayout(device, shadowAlphaPipelineLayout);
405 for (
auto &slot : shadowMaps) {
407 if (slot.framebuffers[i]) {
408 device->destroyFramebuffer(slot.framebuffers[i]);
409 slot.framebuffers[i] = vk::Framebuffer{};
415 device->destroySampler(shadowSampler);
418 if (shadowRenderPass) {
419 device->destroyRenderPass(shadowRenderPass);
420 shadowRenderPass = {};
422 shadowPassCascade = -1;
423 shadowPassDraws.clear();
424 shadowPendingMask = 0;
425 for (
auto &
d : shadowCascadeDraws)
d.
clear();
428void Graphics::destroyGBufferResources() {
429 gbufferPassActive =
false;
430 gbufferPending =
false;
431 gbufferPassDraws.clear();
432 for (
auto &slot : gbufferSlots) {
433 slot.normalTex.gpuHandle =
nullptr;
434 slot.depthColorTex.gpuHandle =
nullptr;
435 slot.albedoTex.gpuHandle =
nullptr;
436 slot.depthTex.gpuHandle =
nullptr;
437 if (slot.framebuffer) {
438 device->destroyFramebuffer(slot.framebuffer);
439 slot.framebuffer = vk::Framebuffer{};
441 destroySampler(device, slot.normalGpu.sampler);
442 destroySampler(device, slot.depthColorGpu.sampler);
443 destroySampler(device, slot.albedoGpu.sampler);
444 destroySampler(device, slot.depthGpu.sampler);
446 gbufferSlots.clear();
448 destroyPipeline(device, gbufferPipeline);
449 destroyPipeline(device, gbufferAlphaPipeline);
450 destroyPipelineLayout(device, gbufferPipelineLayout);
451 if (gbufferRenderPass) {
452 device->destroyRenderPass(gbufferRenderPass);
453 gbufferRenderPass = {};
460void Graphics::destroySceneColorResources() {
461 sceneColorPassOpen =
false;
463 for (
auto &slot : sceneColorSlots) {
464 slot.colorTex.gpuHandle =
nullptr;
465 if (slot.framebuffer) {
466 device->destroyFramebuffer(slot.framebuffer);
467 slot.framebuffer = vk::Framebuffer{};
469 destroySampler(device, slot.colorGpu.sampler);
471 sceneColorSlots.clear();
473 if (sceneColorRenderPass) {
474 device->destroyRenderPass(sceneColorRenderPass);
475 sceneColorRenderPass = {};
478 sceneColorHeight = 0;
479 sceneColorFormat = vk::Format::eUndefined;
480 sceneColorSamples = vk::SampleCountFlagBits::e1;
483void Graphics::createUiColorResources(
int width,
int height) {
485 const vk::Format colorFmt = swapchain.image_format;
486 if (colorFmt == vk::Format::eUndefined)
return;
488 const vk::SampleCountFlags supported =
489 device.physical_device.properties.limits.framebufferColorSampleCounts;
490 const vk::SampleCountFlagBits samples =
491 (supported & vk::SampleCountFlagBits::e4) ? vk::SampleCountFlagBits::e4
492 : vk::SampleCountFlagBits::e1;
498 if (samples != vk::SampleCountFlagBits::e1) {
501 .addColorAttachment(colorFmt, vk::AttachmentLoadOp::eClear,
502 vk::AttachmentStoreOp::eDontCare, samples)
503 .addResolveColorAttachment(colorFmt, vk::AttachmentLoadOp::eDontCare)
504 .addSubpass(vkb::SubpassBuilder()
505 .addAttachmentRef(0, vk::ImageLayout::eColorAttachmentOptimal)
506 .addResolveAttachment(1, vk::ImageLayout::eColorAttachmentOptimal))
507 .addExternalShaderReadDependencies()
512 .addSampledColorAttachment(colorFmt)
513 .addSubpass(vkb::SubpassBuilder()
514 .addAttachmentRef(0, vk::ImageLayout::eColorAttachmentOptimal))
515 .addExternalShaderReadDependencies()
521 uiColorSamples = samples;
523 if (!texSetLayout || !descriptorPool)
return;
525 if (!uiColorSlots.empty() && uiColorWidth ==
width && uiColorHeight ==
height &&
526 uiColorFormat == colorFmt && uiColorSamples == samples)
528 destroyUiColorTargets();
530 uiColorWidth =
width;
532 uiColorFormat = colorFmt;
533 uiColorSamples = samples;
534 const uint32_t
w = uint32_t(
width);
535 const uint32_t
h = uint32_t(
height);
536 const bool msaa = samples != vk::SampleCountFlagBits::e1;
538 uiColorSlots.resize(kAsyncResourceCopies);
539 for (
auto &slot : uiColorSlots) {
540 slot.color =
device.createColorTarget(
w,
h, colorFmt);
541 if (msaa) slot.msaaColor =
device.createColorTarget(
w,
h, colorFmt, samples);
543 auto uiPass = uiRenderPass;
545 for (
auto &slot : uiColorSlots) {
547 slot.framebuffer = uiPass.createFramebuffer(
548 device,
w,
h, {slot.msaaColor.asAttachment(), slot.color.asAttachment()});
550 slot.framebuffer = uiPass.createFramebuffer(device,
w,
h, {slot.color.asAttachment()});
554 auto makeSampleTex = [&](GpuTexture &gpu, Texture &tex, vk::ImageView
view) {
555 vkb::SamplerBuilder sb;
556 gpu.sampler = sb.magFilter(vk::Filter::eLinear)
557 .minFilter(vk::Filter::eLinear)
558 .addressModeU(vk::SamplerAddressMode::eClampToEdge)
559 .addressModeV(vk::SamplerAddressMode::eClampToEdge)
561 auto sets = vkb::DescriptorSetBuilder()
562 .layout(texSetLayout)
563 .build(
device.instance, descriptorPool);
564 gpu.descriptorSet = vkb::BoundSet{sets[0]};
567 gpu.viewOverride =
view;
568 writeCombinedImageDescriptor(&gpu);
571 tex.pixelWidth =
width;
573 tex.gpuHandle = &gpu;
575 for (
auto &slot : uiColorSlots)
576 makeSampleTex(slot.colorGpu, slot.colorTex, slot.
color.imageView());
579void Graphics::destroyUiColorTargets() {
581 for (
auto &slot : uiColorSlots) {
582 slot.colorTex.gpuHandle =
nullptr;
583 if (slot.framebuffer) {
584 device->destroyFramebuffer(slot.framebuffer);
585 slot.framebuffer = vk::Framebuffer{};
587 destroySampler(device, slot.colorGpu.sampler);
589 uiColorSlots.clear();
592 uiColorFormat = vk::Format::eUndefined;
596void Graphics::destroyUiColorResources() {
597 destroyUiColorTargets();
599 device->destroyRenderPass(uiRenderPass);
604void Graphics::queueUiResolve() {
605 auto *slot = currentUiColorSlot();
606 if (!slot || !slot->colorTex.gpuHandle)
return;
607 TexturedBatch resolve{&slot->colorTex,
nullptr,
nullptr,
BlendMode::Alpha, Batcher{}};
608 resolve.batch.addTexturedRect(0.f, 0.f,
float(uiColorWidth),
float(uiColorHeight),
609 Color(1.f, 1.f, 1.f, 1.f), 0.f, 0.f, 1.f, 1.f);
610 pendingUiResolve = std::move(resolve);
613bool Graphics::renderUiOverlayPass() {
615 createUiColorResources(
int(swapchain.extent.width),
int(swapchain.extent.height));
616 auto *slot = currentUiColorSlot();
617 if (!slot || !uiRenderPass || !slot->framebuffer)
return false;
619 auto &cb = currentPresentCb();
620 const bool msaa = uiColorSamples != vk::SampleCountFlagBits::e1;
621 std::array<vk::ClearValue, 2> clears{};
622 clears[0].color = vk::ClearColorValue(std::array<float, 4>{0.f, 0.f, 0.f, 0.f});
623 clears[1].color = vk::ClearColorValue(std::array<float, 4>{0.f, 0.f, 0.f, 0.f});
624 vk::RenderPassBeginInfo rpBegin{};
625 rpBegin.renderPass = uiRenderPass;
626 rpBegin.framebuffer = slot->framebuffer;
627 rpBegin.renderArea = vk::Rect2D{{0, 0}, {uint32_t(uiColorWidth), uint32_t(uiColorHeight)}};
628 rpBegin.clearValueCount = msaa ? 2 : 1;
629 rpBegin.pClearValues = clears.data();
631 slot->color.beginColorAttachment();
632 if (msaa) slot->msaaColor.beginColorAttachment();
633 cb.beginRenderPass(rpBegin, vk::SubpassContents::eInline);
635 VkCommandBuffer raw =
static_cast<VkCommandBuffer
>(cb);
639 slot->color.endSampledLayout();
645void Graphics::createGBufferResources(
int width,
int height) {
647 if (!gbufferSlots.empty() && gbufferWidth ==
width && gbufferHeight ==
height && gbufferPipeline)
649 destroyGBufferResources();
651 gbufferWidth =
width;
653 const uint32_t
w = uint32_t(
width);
654 const uint32_t
h = uint32_t(
height);
655 const vk::Format colorFmt = pickGBufferColorFormat(device);
656 const vk::Format depthFmt = vk::Format::eD32Sfloat;
658 gbufferSlots.resize(kAsyncResourceCopies);
659 for (
auto &slot : gbufferSlots) {
660 slot.normal =
device.createColorTarget(
w,
h, colorFmt);
661 slot.depthColor =
device.createColorTarget(
w,
h, colorFmt);
662 slot.albedo =
device.createColorTarget(
w,
h, colorFmt);
663 slot.depth =
device.createDepthTarget(
w,
h, depthFmt,
true);
668 .addSampledColorAttachment(colorFmt)
669 .addSampledColorAttachment(colorFmt)
670 .addSampledColorAttachment(colorFmt)
671 .addSampledDepthAttachment(depthFmt)
672 .addSubpass(vkb::SubpassBuilder()
673 .addAttachmentRef(0, vk::ImageLayout::eColorAttachmentOptimal)
674 .addAttachmentRef(1, vk::ImageLayout::eColorAttachmentOptimal)
675 .addAttachmentRef(2, vk::ImageLayout::eColorAttachmentOptimal)
676 .setDepthStencilAttachment(
677 3, vk::ImageLayout::eDepthStencilAttachmentOptimal))
678 .addExternalShaderReadDependencies()
680 gbufferRenderPass = gbufferPass;
682 for (
auto &slot : gbufferSlots) {
683 slot.framebuffer = gbufferPass.createFramebuffer(
685 {slot.normal.asAttachment(), slot.depthColor.asAttachment(), slot.albedo.asAttachment(),
686 slot.depth.asAttachment()});
689 auto layoutBuilder =
device.createPipelineLayout();
690 if (texSetLayout) layoutBuilder.set(texSetLayout);
691 gbufferPipelineLayout =
693 .push<GBufferPush>(vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment)
696 std::vector<uint32_t>
vert(mesh3d_gbuffer_vert_spv,
697 mesh3d_gbuffer_vert_spv + mesh3d_gbuffer_vert_spv_count);
698 std::vector<uint32_t>
frag(mesh3d_gbuffer_frag_spv,
699 mesh3d_gbuffer_frag_spv + mesh3d_gbuffer_frag_spv_count);
700 vk::ShaderModule vertModule = vkb::PipelineBuilder::createShaderModule(
device.instance,
vert);
701 vk::ShaderModule fragModule = vkb::PipelineBuilder::createShaderModule(
device.instance,
frag);
702 gbufferPipeline =
device.createPipeline()
703 .useClassicPipeline(vertModule, fragModule)
704 .setPipelineLayout(gbufferPipelineLayout)
705 .setVertexInputState(vkb::VertexInputStateBuilder()
706 .addInputBinding<MeshVertex>()
707 .addAttributeDescription<MeshVertex>())
708 .setDynamicStatesViewportScissor()
709 .setRasterizer(vk::PolygonMode::eFill,
false,
false, 1.0f,
710 vk::CullModeFlagBits::eNone, vk::FrontFace::eClockwise)
712 device->destroyShaderModule(vertModule);
713 device->destroyShaderModule(fragModule);
717 vk::ShaderModule alphaVertModule =
718 vkb::PipelineBuilder::createShaderModule(
device.instance,
vert);
719 std::vector<uint32_t> alphaFrag(mesh3d_gbuffer_alpha_frag_spv,
720 mesh3d_gbuffer_alpha_frag_spv +
721 mesh3d_gbuffer_alpha_frag_spv_count);
722 vk::ShaderModule alphaFragModule =
723 vkb::PipelineBuilder::createShaderModule(
device.instance, alphaFrag);
724 gbufferAlphaPipeline =
device.createPipeline()
725 .useClassicPipeline(alphaVertModule, alphaFragModule)
726 .setPipelineLayout(gbufferPipelineLayout)
727 .setVertexInputState(vkb::VertexInputStateBuilder()
728 .addInputBinding<MeshVertex>()
729 .addAttributeDescription<MeshVertex>())
730 .setDynamicStatesViewportScissor()
731 .setRasterizer(vk::PolygonMode::eFill,
false,
false, 1.0f,
732 vk::CullModeFlagBits::eNone,
733 vk::FrontFace::eClockwise)
735 device->destroyShaderModule(alphaVertModule);
736 device->destroyShaderModule(alphaFragModule);
738 auto makeSampleTex = [&](GpuTexture &gpu, Texture &tex, vk::ImageView
view) {
739 vkb::SamplerBuilder sb;
740 gpu.sampler = sb.nearestClamp().build(device);
741 auto sets = vkb::DescriptorSetBuilder()
742 .layout(texSetLayout)
743 .build(
device.instance, descriptorPool);
744 gpu.descriptorSet = vkb::BoundSet{sets[0]};
747 gpu.viewOverride =
view;
748 writeCombinedImageDescriptor(&gpu);
751 tex.pixelWidth =
width;
753 tex.gpuHandle = &gpu;
755 for (
auto &slot : gbufferSlots) {
756 makeSampleTex(slot.normalGpu, slot.normalTex, slot.normal.imageView());
757 makeSampleTex(slot.depthColorGpu, slot.depthColorTex, slot.depthColor.imageView());
758 makeSampleTex(slot.albedoGpu, slot.albedoTex, slot.albedo.imageView());
759 makeSampleTex(slot.depthGpu, slot.depthTex, slot.depth.imageView());
763void Graphics::createSceneColorResources(
int width,
int height) {
765 if (!texSetLayout || !descriptorPool)
return;
766 const vk::Format colorFmt = swapchain.image_format;
767 if (colorFmt == vk::Format::eUndefined)
return;
771 if (desired != appliedMsaa) appliedMsaa = desired;
772 const vk::SampleCountFlagBits samples = sampleCountFlagFor(clampMsaaSamples(appliedMsaa));
774 if (!sceneColorSlots.empty() && sceneColorWidth ==
width && sceneColorHeight ==
height &&
775 sceneColorFormat == colorFmt && sceneColorSamples == samples && sceneColorRenderPass)
777 destroySceneColorResources();
779 sceneColorWidth =
width;
780 sceneColorHeight =
height;
781 sceneColorFormat = colorFmt;
782 sceneColorSamples = samples;
783 const uint32_t
w = uint32_t(
width);
784 const uint32_t
h = uint32_t(
height);
785 const vk::Format depthFmt = depthFormat;
786 const bool msaa = samples != vk::SampleCountFlagBits::e1;
788 sceneColorSlots.resize(kAsyncResourceCopies);
789 for (
auto &slot : sceneColorSlots) {
790 slot.color =
device.createColorTarget(
w,
h, colorFmt);
792 slot.msaaColor =
device.createColorTarget(
w,
h, colorFmt, samples);
793 slot.depth =
device.createDepthTarget(
w,
h, depthFmt,
false, samples);
795 slot.depth =
device.createDepthTarget(
w,
h, depthFmt,
false);
803 sceneColorRenderPass =
805 .addColorAttachment(colorFmt, vk::AttachmentLoadOp::eClear,
806 vk::AttachmentStoreOp::eDontCare, samples)
807 .addResolveColorAttachment(colorFmt, vk::AttachmentLoadOp::eDontCare)
808 .addDepthAttachment(depthFmt, vk::AttachmentLoadOp::eClear,
809 vk::AttachmentStoreOp::eDontCare, samples)
810 .addSubpass(vkb::SubpassBuilder()
811 .addAttachmentRef(0, vk::ImageLayout::eColorAttachmentOptimal)
812 .addResolveAttachment(1, vk::ImageLayout::eColorAttachmentOptimal)
813 .setDepthStencilAttachment(
814 2, vk::ImageLayout::eDepthStencilAttachmentOptimal))
815 .addExternalShaderReadDependencies()
818 sceneColorRenderPass =
820 .addSampledColorAttachment(colorFmt)
821 .addDepthAttachment(depthFmt, vk::AttachmentLoadOp::eClear,
822 vk::AttachmentStoreOp::eDontCare)
823 .addSubpass(vkb::SubpassBuilder()
824 .addAttachmentRef(0, vk::ImageLayout::eColorAttachmentOptimal)
825 .setDepthStencilAttachment(
826 1, vk::ImageLayout::eDepthStencilAttachmentOptimal))
827 .addExternalShaderReadDependencies()
830 auto scenePass = sceneColorRenderPass;
832 for (
auto &slot : sceneColorSlots) {
834 slot.framebuffer = scenePass.createFramebuffer(
836 {slot.msaaColor.asAttachment(), slot.color.asAttachment(), slot.depth.asAttachment()});
838 slot.framebuffer = scenePass.createFramebuffer(
839 device,
w,
h, {slot.color.asAttachment(), slot.depth.asAttachment()});
843 auto makeSampleTex = [&](GpuTexture &gpu, Texture &tex, vk::ImageView
view) {
844 vkb::SamplerBuilder sb;
845 gpu.sampler = sb.magFilter(vk::Filter::eLinear)
846 .minFilter(vk::Filter::eLinear)
847 .addressModeU(vk::SamplerAddressMode::eClampToEdge)
848 .addressModeV(vk::SamplerAddressMode::eClampToEdge)
850 auto sets = vkb::DescriptorSetBuilder()
851 .layout(texSetLayout)
852 .build(
device.instance, descriptorPool);
853 gpu.descriptorSet = vkb::BoundSet{sets[0]};
856 gpu.viewOverride =
view;
857 writeCombinedImageDescriptor(&gpu);
860 tex.pixelWidth =
width;
862 tex.gpuHandle = &gpu;
864 for (
auto &slot : sceneColorSlots) makeSampleTex(slot.colorGpu, slot.colorTex, slot.
color.imageView());
867bool Graphics::beginSceneColorRenderPass() {
868 auto *slot = currentSceneColorSlot();
869 if (!slot || !sceneColorRenderPass || !slot->framebuffer)
return false;
870 auto &cb = currentPresentCb();
871 const bool msaa = sceneColorSamples != vk::SampleCountFlagBits::e1;
874 std::array<vk::ClearValue, 3> clears{};
875 clears[0].color = vk::ClearColorValue(
876 std::array<float, 4>{clearColor.r, clearColor.g, clearColor.b, 1.f});
877 clears[1].color = vk::ClearColorValue(
878 std::array<float, 4>{clearColor.r, clearColor.g, clearColor.b, 1.f});
879 clears[2].depthStencil = vk::ClearDepthStencilValue{1.0f, 0};
880 vk::RenderPassBeginInfo rpBegin{};
881 rpBegin.renderPass = sceneColorRenderPass;
882 rpBegin.framebuffer = slot->framebuffer;
884 vk::Rect2D{{0, 0}, {uint32_t(sceneColorWidth), uint32_t(sceneColorHeight)}};
885 rpBegin.clearValueCount = uint32_t(clears.size());
886 rpBegin.pClearValues = clears.data();
887 slot->msaaColor.beginColorAttachment();
888 slot->color.beginColorAttachment();
889 slot->depth.beginDepthAttachment();
890 cb.beginRenderPass(rpBegin, vk::SubpassContents::eInline);
892 std::array<vk::ClearValue, 2> clears{};
893 clears[0].color = vk::ClearColorValue(
894 std::array<float, 4>{clearColor.r, clearColor.g, clearColor.b, 1.f});
895 clears[1].depthStencil = vk::ClearDepthStencilValue{1.0f, 0};
896 vk::RenderPassBeginInfo rpBegin{};
897 rpBegin.renderPass = sceneColorRenderPass;
898 rpBegin.framebuffer = slot->framebuffer;
900 vk::Rect2D{{0, 0}, {uint32_t(sceneColorWidth), uint32_t(sceneColorHeight)}};
901 rpBegin.clearValueCount = uint32_t(clears.size());
902 rpBegin.pClearValues = clears.data();
903 slot->color.beginColorAttachment();
904 slot->depth.beginDepthAttachment();
905 cb.beginRenderPass(rpBegin, vk::SubpassContents::eInline);
907 sceneColorPassOpen =
true;
911void Graphics::endSceneColorRenderPass() {
912 if (!sceneColorPassOpen)
return;
913 auto &cb = currentPresentCb();
915 sceneColorPassOpen =
false;
916 if (
auto *slot = currentSceneColorSlot())
917 slot->color.endSampledLayout();
920int Graphics::clampMsaaSamples(
int requested)
const {
921 if (requested <= 1)
return 0;
922 vk::SampleCountFlags supported =
923 device.physical_device.properties.limits.framebufferColorSampleCounts &
924 device.physical_device.properties.limits.framebufferDepthSampleCounts;
925 const int candidates[3] = {8, 4, 2};
926 for (
int c : candidates) {
927 if (requested >=
c && (supported & sampleCountFlagFor(
c)))
return c;
929 for (
int c : candidates) {
930 if (supported & sampleCountFlagFor(
c))
return c;
935void Graphics::ensureScenePassPipelines(
const vkb::BuiltRenderPass &target,
936 vk::SampleCountFlagBits samples) {
937 const vk::RenderPass targetHandle = vk::RenderPass(target);
938 if (targetHandle == scenePassPipelineTarget && samples == scenePassPipelineSamples)
return;
941 destroyPipeline(device, mesh3dPipeline);
942 destroyPipeline(device, mesh3dClusteredPipeline);
943 destroyPipeline(device, voxelRectPipeline);
945 mesh3dPipeline = createMesh3DStylePipeline(embeddedSpirv(mesh3d_vert_spv),
946 embeddedSpirv(mesh3d_frag_spv),
947 mesh3dPipelineLayout, target, samples);
948 mesh3dClusteredPipeline =
949 createMesh3DStylePipeline(embeddedSpirv(mesh3d_clustered_vert_spv),
950 embeddedSpirv(mesh3d_clustered_frag_spv),
951 mesh3dClusteredPipelineLayout, target, samples);
952 voxelRectPipeline = buildVoxelRectPipeline(target, samples);
954 for (
auto &g : ownedGpuShaders) {
955 if (!g->isMesh3D)
continue;
956 destroyPipeline(device, g->mesh3dPipeline);
957 destroyPipeline(device, g->mesh3dXrayPipeline);
958 g->mesh3dXrayPipeline =
nullptr;
959 if (!g->owner)
continue;
962 createMesh3DHairPipeline(g->owner->vertexSpirv(), g->owner->fragmentSpirv(),
963 g->pipelineLayout, target, samples);
966 createMesh3DStylePipeline(g->owner->vertexSpirv(), g->owner->fragmentSpirv(),
967 g->pipelineLayout, target, samples);
968 g->mesh3dXrayPipeline =
969 createMesh3DXrayPipeline(g->owner->vertexSpirv(), g->owner->fragmentSpirv(),
970 g->pipelineLayout, target, samples);
974 scenePassPipelineTarget = targetHandle;
975 scenePassPipelineSamples = samples;
978void Graphics::queueSceneColorResolve() {
980 if (!src || !src->gpuHandle)
return;
983 if (sceneColorComposited)
return;
985 resolve.batch.addTexturedRect(0.f, 0.f,
float(
width),
float(
height),
Color(1.f, 1.f, 1.f, 1.f),
987 pendingSceneResolve = std::move(resolve);
990void Graphics::createShadowResources() {
991 if (!shadowMaps.empty())
return;
996 shadowMaps.resize(kAsyncResourceCopies);
997 for (
auto &slot : shadowMaps)
998 slot.
image =
device.createDepthArray(size, size, layers, vk::Format::eD32Sfloat);
1000 vkb::SamplerBuilder sb;
1004 shadowSampler = sb.linearClamp().compareEnable(VK_TRUE).compareOp(vk::CompareOp::eLess).buildDepthPcf(device);
1007 device.createRenderPass()
1008 .addSampledDepthAttachment(vk::Format::eD32Sfloat)
1009 .addSubpass(vkb::SubpassBuilder().setDepthStencilAttachment(
1010 0, vk::ImageLayout::eDepthStencilAttachmentOptimal))
1011 .addExternalShaderReadDependencies()
1013 shadowRenderPass = shadowPass;
1015 for (
auto &slot : shadowMaps) {
1016 for (uint32_t i = 0; i < layers; ++i) {
1017 slot.framebuffers[i] = shadowPass.createFramebuffer(
1018 device, size, size, {slot.image.layerAttachment(i)});
1022 shadowPipelineLayout =
1023 device.createPipelineLayout()
1024 .push<glm::mat4>(vk::ShaderStageFlagBits::eVertex)
1027 std::vector<uint32_t>
vert(mesh3d_shadow_vert_spv,
1028 mesh3d_shadow_vert_spv + mesh3d_shadow_vert_spv_count);
1029 std::vector<uint32_t>
frag(mesh3d_shadow_frag_spv,
1030 mesh3d_shadow_frag_spv + mesh3d_shadow_frag_spv_count);
1031 vk::ShaderModule vertModule = vkb::PipelineBuilder::createShaderModule(
device.instance,
vert);
1032 vk::ShaderModule fragModule = vkb::PipelineBuilder::createShaderModule(
device.instance,
frag);
1035 .useClassicPipeline(vertModule, fragModule)
1036 .setPipelineLayout(shadowPipelineLayout)
1037 .setVertexInputState(vkb::VertexInputStateBuilder()
1038 .addInputBinding<MeshVertex>()
1039 .addAttributeDescription<MeshVertex>())
1040 .setDynamicStatesViewportScissor()
1044 .setRasterizer(vk::PolygonMode::eFill,
false,
false, 1.0f, vk::CullModeFlagBits::eNone,
1045 vk::FrontFace::eClockwise)
1046 .setDepthBias(0.0f, 0.5f)
1048 device->destroyShaderModule(vertModule);
1049 device->destroyShaderModule(fragModule);
1054 shadowAlphaPipelineLayout =
device.createPipelineLayout()
1056 .push<glm::mat4>(vk::ShaderStageFlagBits::eVertex)
1058 std::vector<uint32_t> alphaVert(mesh3d_shadow_alpha_vert_spv,
1059 mesh3d_shadow_alpha_vert_spv +
1060 mesh3d_shadow_alpha_vert_spv_count);
1061 std::vector<uint32_t> alphaFrag(mesh3d_shadow_alpha_frag_spv,
1062 mesh3d_shadow_alpha_frag_spv +
1063 mesh3d_shadow_alpha_frag_spv_count);
1064 vk::ShaderModule alphaVertModule =
1065 vkb::PipelineBuilder::createShaderModule(
device.instance, alphaVert);
1066 vk::ShaderModule alphaFragModule =
1067 vkb::PipelineBuilder::createShaderModule(
device.instance, alphaFrag);
1068 shadowAlphaPipeline =
1070 .useClassicPipeline(alphaVertModule, alphaFragModule)
1071 .setPipelineLayout(shadowAlphaPipelineLayout)
1072 .setVertexInputState(vkb::VertexInputStateBuilder()
1073 .addInputBinding<MeshVertex>()
1074 .addAttributeDescription<MeshVertex>())
1075 .setDynamicStatesViewportScissor()
1076 .setRasterizer(vk::PolygonMode::eFill,
false,
false, 1.0f, vk::CullModeFlagBits::eNone,
1077 vk::FrontFace::eClockwise)
1078 .setDepthBias(0.0f, 0.5f)
1080 device->destroyShaderModule(alphaVertModule);
1081 device->destroyShaderModule(alphaFragModule);
1085 vkb::executeImmediately(
device.instance, uploadPool,
device.getQueue(vkb::QueueType::graphics),
1086 [&](vk::CommandBuffer cb) {
1087 vk::ClearValue clear{};
1088 clear.depthStencil = vk::ClearDepthStencilValue{1.0f, 0};
1089 for (
auto &slot : shadowMaps) {
1090 slot.image.beginDepthAttachment();
1092 vk::RenderPassBeginInfo rpBegin{};
1093 rpBegin.renderPass = shadowRenderPass;
1094 rpBegin.framebuffer = slot.framebuffers[
c];
1095 rpBegin.renderArea = vk::Rect2D{{0, 0}, {size, size}};
1096 rpBegin.clearValueCount = 1;
1097 rpBegin.pClearValues = &
clear;
1098 cb.beginRenderPass(rpBegin, vk::SubpassContents::eInline);
1101 slot.image.endSampledLayout();
1106void Graphics::ensureClusteredBuffers(
size_t lightsBytes,
size_t tableBytes,
size_t indicesBytes) {
1107 auto &st = currentClusteredStorage();
1108 auto ensure = [&](vkb::GenericBuffer &buf,
size_t &cap,
size_t need) {
1109 if (need == 0) need = 4;
1110 if (cap >= need && buf.buffer)
return;
1113 size_t alloc = std::max(need, cap ? cap * 2 : need);
1114 buf.allocate(frameToken(),
device, vk::BufferUsageFlagBits::eStorageBuffer, vk::DeviceSize(alloc),
1115 vk::MemoryPropertyFlagBits::eHostVisible |
1116 vk::MemoryPropertyFlagBits::eHostCoherent);
1119 ensure(st.lightsBuf, st.lightsCap, lightsBytes);
1120 ensure(st.tableBuf, st.tableCap, tableBytes);
1121 ensure(st.indicesBuf, st.indicesCap, indicesBytes);
1124 currentMesh3dClusteredFrameSlots().sets.clear();
1127void Graphics::uploadClusteredLighting(
const ClusteredLightingUpload &upload) {
1128 const size_t lightsBytes =
1130 const size_t tableBytes =
1131 std::max(
size_t(1), upload.clusterTable.size()) *
sizeof(ClusterTableEntry);
1132 const size_t indicesBytes =
1133 std::max(
size_t(1), upload.lightIndices.size()) *
sizeof(uint32_t);
1134 ensureClusteredBuffers(lightsBytes, tableBytes, indicesBytes);
1136 auto &st = currentClusteredStorage();
1137 if (!upload.lights.empty())
1138 st.lightsBuf.updateLocal(frameToken(), upload.lights.data(),
1142 st.lightsBuf.updateLocal(frameToken(), &zero,
sizeof(zero));
1144 st.tableBuf.updateLocal(frameToken(), upload.clusterTable.data(),
1145 upload.clusterTable.size() *
sizeof(ClusterTableEntry));
1146 st.indicesBuf.updateLocal(frameToken(), upload.lightIndices.data(),
1147 upload.lightIndices.size() *
sizeof(uint32_t));
1151 mesh3dClusteredActive = upload.
active;
1152 mesh3dClustered = upload;
1153 if (upload.
active) uploadClusteredLighting(upload);
1157 mesh3dClusteredActive =
active;
1160void Graphics::ensureMesh3dStrides() {
1161 if (mesh3dUboStride != 0)
return;
1162 const uint32_t align = std::max(
1163 1u, uint32_t(
device.physical_device.properties.limits.minUniformBufferOffsetAlignment));
1164 mesh3dUboStride = alignUpValue(uint32_t(
sizeof(
Mesh3DUBO)), align);
1165 shadowUboStride = alignUpValue(uint32_t(
sizeof(
ShadowUBO)), align);
1166 mesh3dClusteredUboStride = alignUpValue(uint32_t(
sizeof(
Mesh3DClusteredUBO)), align);
1169void Graphics::ensureMesh3dRing(Mesh3dFrameSlots &fslots) {
1170 ensureMesh3dStrides();
1171 const size_t want = std::max<size_t>(2048, fslots.lastDrawCount + 512);
1172 if (fslots.uboRing.buffer && fslots.capacity >= want)
return;
1173 const size_t cap = std::max(want, fslots.capacity * 2);
1176 fslots.uboRing.release();
1177 fslots.uboRing.allocate(frameToken(),
device, vk::BufferUsageFlagBits::eUniformBuffer,
1178 vk::DeviceSize(cap) * mesh3dUboStride, kHostVisibleCoherent);
1179 fslots.shadowRing.release();
1180 fslots.shadowRing.allocate(frameToken(),
device, vk::BufferUsageFlagBits::eUniformBuffer,
1181 vk::DeviceSize(cap) * shadowUboStride, kHostVisibleCoherent);
1182 fslots.capacity = cap;
1183 fslots.sets.clear();
1186void Graphics::ensureMesh3dClusteredRing(Mesh3dClusteredFrameSlots &fslots) {
1187 ensureMesh3dStrides();
1188 const size_t want = std::max<size_t>(2048, fslots.lastDrawCount + 512);
1189 if (fslots.uboRing.buffer && fslots.capacity >= want)
return;
1190 const size_t cap = std::max(want, fslots.capacity * 2);
1191 fslots.uboRing.release();
1192 fslots.uboRing.allocate(frameToken(),
device, vk::BufferUsageFlagBits::eUniformBuffer,
1193 vk::DeviceSize(cap) * mesh3dClusteredUboStride, kHostVisibleCoherent);
1194 fslots.shadowRing.release();
1195 fslots.shadowRing.allocate(frameToken(),
device, vk::BufferUsageFlagBits::eUniformBuffer,
1196 vk::DeviceSize(cap) * shadowUboStride, kHostVisibleCoherent);
1197 fslots.capacity = cap;
1198 fslots.sets.clear();
1201vkb::BoundSet Graphics::mesh3dClusteredSetFor(GpuTexture *gpuTex, GpuTexture *normalTex,
1202 GpuTexture *envTex, GpuTexture *heightTex,
1203 Mesh3dClusteredFrameSlots &fslots) {
1204 ASSERT(gpuTex !=
nullptr);
1205 ASSERT(normalTex !=
nullptr);
1206 ASSERT(envTex !=
nullptr);
1207 ASSERT(heightTex !=
nullptr);
1208 ASSERT(currentShadowArrayView());
1209 ASSERT(fslots.uboRing.buffer);
1210 ASSERT(fslots.shadowRing.buffer);
1212 Mesh3dSetKey key{gpuTex, normalTex, envTex, heightTex,
nullptr};
1213 auto it = fslots.sets.find(key);
1214 if (it != fslots.sets.end())
return it->second;
1216 vk::DescriptorSetAllocateInfo alloc{};
1217 alloc.descriptorPool = descriptorPool;
1218 alloc.descriptorSetCount = 1;
1219 alloc.pSetLayouts = &mesh3dClusteredSetLayout;
1220 vkb::UnboundSet unbound{
device->allocateDescriptorSets(alloc).front()};
1222 auto &st = currentClusteredStorage();
1223 vkb::DescriptorSetUpdater updater(14, 14, 0);
1224 updater.beginDescriptorSet(unbound)
1225 .beginBuffers(0, 0, vk::DescriptorType::eUniformBufferDynamic)
1226 .buffer(fslots.uboRing.buffer, 0, fslots.uboRing.size)
1227 .beginImages(1, 0, vk::DescriptorType::eCombinedImageSampler)
1228 .image(vkb::SampledImage::forLaterSample(gpuTex->sampler, gpuTex->imageView()))
1229 .beginImages(2, 0, vk::DescriptorType::eCombinedImageSampler)
1230 .image(vkb::SampledImage::forLaterSample(normalTex->sampler, normalTex->imageView()))
1231 .beginImages(3, 0, vk::DescriptorType::eCombinedImageSampler)
1232 .image(vkb::SampledImage::forLaterSample(envTex->sampler, envTex->imageView()))
1233 .beginBuffers(4, 0, vk::DescriptorType::eStorageBuffer)
1234 .buffer(st.lightsBuf.buffer, 0, vk::DeviceSize(st.lightsCap))
1235 .beginBuffers(5, 0, vk::DescriptorType::eStorageBuffer)
1236 .buffer(st.tableBuf.buffer, 0, vk::DeviceSize(st.tableCap))
1237 .beginBuffers(6, 0, vk::DescriptorType::eStorageBuffer)
1238 .buffer(st.indicesBuf.buffer, 0, vk::DeviceSize(st.indicesCap))
1239 .beginBuffers(7, 0, vk::DescriptorType::eUniformBufferDynamic)
1240 .buffer(fslots.shadowRing.buffer, 0, fslots.shadowRing.size)
1241 .beginImages(8, 0, vk::DescriptorType::eCombinedImageSampler)
1242 .image(vkb::SampledImage::forLaterSample(shadowSampler, currentShadowArrayView()))
1243 .beginImages(9, 0, vk::DescriptorType::eCombinedImageSampler)
1244 .image(vkb::SampledImage::forLaterSample(heightTex->sampler, heightTex->imageView()))
1245 .update(
device.instance);
1247 vkb::BoundSet bound = std::move(unbound).publish();
1248 fslots.sets.emplace(key, bound);
1252vk::Pipeline Graphics::createMesh3DStylePipeline(
const std::vector<uint32_t> &
vert,
1253 const std::vector<uint32_t> &
frag,
1254 vk::PipelineLayout
layout,
1255 const vkb::BuiltRenderPass &rp,
1256 vk::SampleCountFlagBits samples) {
1257 vk::ShaderModule vertModule = vkb::PipelineBuilder::createShaderModule(
device.instance,
vert);
1258 vk::ShaderModule fragModule = vkb::PipelineBuilder::createShaderModule(
device.instance,
frag);
1259 vk::Pipeline pipeline =
1261 .useClassicPipeline(vertModule, fragModule)
1262 .setPipelineLayout(
layout)
1263 .setVertexInputState(vkb::VertexInputStateBuilder()
1264 .addInputBinding<MeshVertex>()
1265 .addAttributeDescription<MeshVertex>())
1266 .setDynamicStatesViewportScissor()
1267 .setRasterizer(vk::PolygonMode::eFill,
false,
false, 1.0f, vk::CullModeFlagBits::eNone,
1268 vk::FrontFace::eClockwise)
1269 .setMultisampler(
false, samples)
1270 .setDepthStencil(
true,
true, vk::CompareOp::eLess)
1271 .setColorAttachmentCount(1)
1273 device->destroyShaderModule(vertModule);
1274 device->destroyShaderModule(fragModule);
1278vk::Pipeline Graphics::createMesh3DHairPipeline(
const std::vector<uint32_t> &
vert,
1279 const std::vector<uint32_t> &
frag,
1280 vk::PipelineLayout
layout,
1281 const vkb::BuiltRenderPass &rp,
1282 vk::SampleCountFlagBits samples) {
1283 vk::ShaderModule vertModule = vkb::PipelineBuilder::createShaderModule(
device.instance,
vert);
1284 vk::ShaderModule fragModule = vkb::PipelineBuilder::createShaderModule(
device.instance,
frag);
1285 vk::Pipeline pipeline =
1287 .useClassicPipeline(vertModule, fragModule)
1288 .setPipelineLayout(
layout)
1289 .setVertexInputState(vkb::VertexInputStateBuilder()
1290 .addInputBinding<MeshVertex>()
1291 .addAttributeDescription<MeshVertex>())
1292 .setDynamicStatesViewportScissor()
1293 .setRasterizer(vk::PolygonMode::eFill,
false,
false, 1.0f, vk::CullModeFlagBits::eNone,
1294 vk::FrontFace::eClockwise)
1295 .setDepthBias(-1.5f, -1.0f)
1296 .setMultisampler(
false, samples)
1297 .setDepthStencil(
true,
false, vk::CompareOp::eLess)
1298 .setAlphaBlending(1)
1300 device->destroyShaderModule(vertModule);
1301 device->destroyShaderModule(fragModule);
1305vk::Pipeline Graphics::createMesh3DXrayPipeline(
const std::vector<uint32_t> &
vert,
1306 const std::vector<uint32_t> &
frag,
1307 vk::PipelineLayout
layout,
1308 const vkb::BuiltRenderPass &rp,
1309 vk::SampleCountFlagBits samples) {
1310 vk::ShaderModule vertModule = vkb::PipelineBuilder::createShaderModule(
device.instance,
vert);
1311 vk::ShaderModule fragModule = vkb::PipelineBuilder::createShaderModule(
device.instance,
frag);
1312 vk::Pipeline pipeline =
1314 .useClassicPipeline(vertModule, fragModule)
1315 .setPipelineLayout(
layout)
1316 .setVertexInputState(vkb::VertexInputStateBuilder()
1317 .addInputBinding<MeshVertex>()
1318 .addAttributeDescription<MeshVertex>())
1319 .setDynamicStatesViewportScissor()
1320 .setRasterizer(vk::PolygonMode::eFill,
false,
false, 1.0f, vk::CullModeFlagBits::eNone,
1321 vk::FrontFace::eClockwise)
1322 .setMultisampler(
false, samples)
1325 .setDepthStencil(
false,
false, vk::CompareOp::eLess)
1326 .setAlphaBlending(1)
1327 .setColorAttachmentCount(1)
1329 device->destroyShaderModule(vertModule);
1330 device->destroyShaderModule(fragModule);
1334void Graphics::ensureOffscreenPipelines() {
1335 if (offscreenRenderPass)
return;
1337 vkb::RenderPassBuilder rpBuilder =
device.createRenderPass();
1338 offscreenRenderPass =
1339 rpBuilder.addSampledColorAttachment(vk::Format::eR8G8B8A8Unorm)
1340 .addSubpass(vkb::SubpassBuilder().addAttachmentRef(0, vk::ImageLayout::eColorAttachmentOptimal))
1341 .addDependency(VK_SUBPASS_EXTERNAL, 0)
1344 offscreenSolidPipeline =
1345 createSolidColorPipeline(
device, offscreenRenderPass, pipelineLayout);
1346 offscreenSolidAlphaPipeline =
1348 offscreenAdditiveSolidPipeline =
1352 auto tvert = embeddedSpirv(textured_vert_spv);
1353 auto tfrag = embeddedSpirv(textured_frag_spv);
1354 offscreenTexPipeline =
1355 createTexturedStylePipeline(tvert, tfrag, offscreenRenderPass, texPipelineLayout);
1356 offscreenAdditiveTexPipeline =
1357 createTexturedStylePipeline(tvert, tfrag, offscreenRenderPass, texPipelineLayout,
1359 offscreenOpaqueTexPipeline =
1360 createTexturedStylePipeline(tvert, tfrag, offscreenRenderPass, texPipelineLayout,
1363 if (lit2dPipelineLayout) {
1364 auto lvert = embeddedSpirv(lit2d_vert_spv);
1365 auto lfrag = embeddedSpirv(lit2d_frag_spv);
1366 offscreenLitPipeline =
1367 createTexturedStylePipeline(lvert, lfrag, offscreenRenderPass, lit2dPipelineLayout);
1371 for (
auto &sh : ownedShaders) ensureShaderOffscreenPipeline(sh.get());
1374void Graphics::ensureShaderOffscreenPipeline(Shader *
shader) {
1375 if (!
shader || !
shader->gpuHandle || !offscreenRenderPass)
return;
1378 auto *gpu =
static_cast<GpuShader *
>(
shader->gpuHandle);
1379 if (gpu->isMesh3D || gpu->offscreenPipeline)
return;
1380 gpu->offscreenPipeline = createTexturedStylePipeline(
shader->vertexSpirv(),
shader->fragmentSpirv(),
1381 offscreenRenderPass, shaderPipelineLayout);
image::ImageData::Colorf color
virtual Texture * getTexture()=0
Sampleable color buffer; screen Canvas returns nullptr.
virtual void setMesh3DClusteredActive(bool active)=0
Cheap per-draw toggle for the already-uploaded clustered light table. Unlike setMesh3DClusteredLighti...
Shader * prepareSceneColorResolveShader(Texture *scene)
FXAA resolve shader that writes opaque RGB (ignores scene-color depth alpha).
virtual void setMesh3DClusteredLighting(const ClusteredLightingUpload &upload)=0
Enable clustered forward path for subsequent default mesh draws (SSBO light lists)....
void * presentOverlayUser_
std::unique_ptr< RenderControl > renderControl_
PresentOverlayFn presentOverlayFn_
static constexpr uint32_t kPushConstantBytes
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
Texture * getSceneColorTexture() override
Sampleable 3D color target for the current frame (RGB = lit, A = linear depth). Valid after begin3DFr...
void clear(std::optional< Color > color, std::optional< int > stencil, std::optional< double > depth) override
Light3DGpu ClusteredLightGpu
glm::vec4 Color
RGBA color used by every graphics draw call. Lives inside eve::graphics so including a graphics heade...
BlendMode
2D quad blend mode (drawn in draw order within a layer).
CPU-built clustered lighting upload for one frame/camera. Point lights are clustered; directional lig...
static constexpr int kMapSize
static constexpr int kCascades
Per-frame / per-draw CSM constants (std140). Binding separate from Mesh3D Frame UBO.