载入中...
搜索中...
未找到
Graphics.cpp
浏览该文件的文档.
1// Vulkan backend implementation — backend lifecycle and frame orchestration.
2//
3// Re-split from the merged dev single-TU Graphics.cpp (pure move;
4// dev changes preserved). Shared helpers live in GraphicsInternal.h.
5
6#define VKB_IMPL
11#include "graphics/Light.h"
12#include "graphics/Outline.h"
15
16#include <SDL2/SDL.h>
17#include <SDL2/SDL_vulkan.h>
18
19#include <algorithm>
20#include <array>
21#include <cmath>
22#include <cstdio>
23#include <cstdlib>
24#include <cstring>
25#include <functional>
26#include <stdexcept>
27#include <string>
28#include <vector>
29#if !defined(_WIN32)
30#include <unistd.h>
31#endif
32
33#include "common/Exception.h"
35#include "common/config.h"
37#include "image/Image.h"
38#include "image/ImageData.h"
39#include "zeroerr/assert.h"
40
41#include <memory>
42
43
44#include <assimp/mesh.h>
45#include <assimp/matrix3x3.h>
46#include <assimp/matrix4x4.h>
47#include <assimp/vector3.h>
48#include <glm/gtc/matrix_transform.hpp>
49
51
52namespace eve::graphics::vulkan {
53
54// --- Backend lifecycle and frame orchestration --------------------------------
55
56std::string Graphics::getBackendName() const { return "vulkan"; }
57
59 if (!initialized) return;
60 device->waitIdle();
61 // Pipeline objects hold raw Shader* owned by ownedShaders. Drop them
62 // first so their destructors do not see freed shader pointers.
63 pipelineAA_.reset();
64 pipelineAO_.reset();
65 pipelineGI_.reset();
66 renderControl_.reset();
67 ownedCanvases.clear();
68 ownedMeshes.clear();
69 ownedGpuMeshes.clear();
70 ownedTextures.clear();
71 for (auto &g : ownedGpuTextures) {
72 if (g->sampler) device->destroySampler(g->sampler);
73 }
74 ownedGpuTextures.clear();
75 texturesByPath.clear();
76 for (auto &g : ownedGpuShaders) {
77 if (g->swapchainPipeline) device->destroyPipeline(g->swapchainPipeline);
78 if (g->offscreenPipeline) device->destroyPipeline(g->offscreenPipeline);
79 if (g->mesh3dPipeline) device->destroyPipeline(g->mesh3dPipeline);
80 if (g->mesh3dXrayPipeline) device->destroyPipeline(g->mesh3dXrayPipeline);
81 // pipelineLayout is shared; do not destroy per-shader
82 }
83 ownedGpuShaders.clear();
84 ownedShaders.clear();
85 destroySwapchainResources();
86 if (pipeline) device->destroyPipeline(pipeline);
87 if (solidAlphaPipeline) device->destroyPipeline(solidAlphaPipeline);
88 if (additiveSolidPipeline) device->destroyPipeline(additiveSolidPipeline);
89 if (pipelineLayout) device->destroyPipelineLayout(pipelineLayout);
90 if (texPipeline) device->destroyPipeline(texPipeline);
91 if (additiveTexPipeline) device->destroyPipeline(additiveTexPipeline);
92 if (opaqueTexPipeline) device->destroyPipeline(opaqueTexPipeline);
93 if (texPipelineLayout) device->destroyPipelineLayout(texPipelineLayout);
94 if (shaderPipelineLayout) device->destroyPipelineLayout(shaderPipelineLayout);
95 if (mesh3dPipeline) device->destroyPipeline(mesh3dPipeline);
96 destroyOffscreen3DResources();
97 if (mesh3dPipelineLayout) device->destroyPipelineLayout(mesh3dPipelineLayout);
98 if (mesh3dShaderPipelineLayout) device->destroyPipelineLayout(mesh3dShaderPipelineLayout);
99 mesh3dSetLayoutUnique.reset();
100 mesh3dFrameSlots.clear();
101 destroyVoxelRectResources();
102 if (mesh3dClusteredPipeline) device->destroyPipeline(mesh3dClusteredPipeline);
103 if (mesh3dClusteredPipelineLayout) device->destroyPipelineLayout(mesh3dClusteredPipelineLayout);
104 mesh3dClusteredSetLayoutUnique.reset();
105 mesh3dClusteredFrameSlots.clear();
106 destroyShadowResources();
107 destroyGBufferResources();
108 destroySceneColorResources();
109 destroyUiColorResources();
110 auto destroyBuf = [&](vkb::GenericBuffer &b) { b.release(); };
111 for (auto &st : clusteredStorages) {
112 destroyBuf(st.lightsBuf);
113 destroyBuf(st.tableBuf);
114 destroyBuf(st.indicesBuf);
115 }
116 clusteredStorages.clear();
117 if (lit2dPipeline) device->destroyPipeline(lit2dPipeline);
118 if (offscreenLitPipeline) device->destroyPipeline(offscreenLitPipeline);
119 if (lit2dPipelineLayout) device->destroyPipelineLayout(lit2dPipelineLayout);
120 lit2dSetLayoutUnique.reset();
121 for (auto &m : lit2dSets) m.clear();
122 lit2dSets.clear();
123 offscreenLit2dSets.clear();
124 destroyBuf(offscreenLighting2dUbo);
125 if (offscreenSolidPipeline) device->destroyPipeline(offscreenSolidPipeline);
126 if (offscreenSolidAlphaPipeline) device->destroyPipeline(offscreenSolidAlphaPipeline);
127 if (offscreenAdditiveSolidPipeline) device->destroyPipeline(offscreenAdditiveSolidPipeline);
128 if (offscreenTexPipeline) device->destroyPipeline(offscreenTexPipeline);
129 if (offscreenAdditiveTexPipeline) device->destroyPipeline(offscreenAdditiveTexPipeline);
130 if (offscreenOpaqueTexPipeline) device->destroyPipeline(offscreenOpaqueTexPipeline);
131 if (offscreenRenderPass) device->destroyRenderPass(offscreenRenderPass);
132 texSetLayoutUnique.reset();
133 if (descriptorPool) device->destroyDescriptorPool(descriptorPool);
134 if (uploadPool) device->destroyCommandPool(uploadPool);
135 if (renderpass) device->destroyRenderPass(renderpass);
136 if (surface) inst.instance.destroySurfaceKHR(surface);
137}
138
139void Graphics::initWithWindow(void *nativeWindow) {
140 StartupStage initStage("graphics: initWithWindow (total)");
141 if (initialized) {
142 // Window module may destroy/recreate the SDL window (tests, setWindowSettings).
143 // The Vulkan surface is tied to the native window. SDL can hand back the
144 // same pointer for a freshly recreated window, so pointer identity alone
145 // can't tell whether the surface is still valid: Window::close() drops it
146 // via onNativeWindowDestroyed(), and a missing surface must be rebuilt too.
147 if (sdlWindow == nativeWindow && surface) return;
148 sdlWindow = nativeWindow;
149 recreateSurfaceForResume();
150 // Restore the initWithWindow contract (device + swapchain valid on
151 // return): rebuild the swapchain synchronously. On desktop this runs
152 // immediately; on Android/iOS isRenderSurfaceStable() defers it until
153 // the native surface settles, leaving swapchainDirty set (same as before).
154 rebuildSwapchainIfNeeded();
155 return;
156 }
157 sdlWindow = nativeWindow;
158 auto *window = static_cast<SDL_Window *>(nativeWindow);
159 ASSERT(window != nullptr);
160 if (!window) throw Exception("Graphics::initWithWindow: null SDL_Window");
161
162 unsigned int count = 0;
163 if (!SDL_Vulkan_GetInstanceExtensions(window, &count, nullptr))
164 throw Exception("SDL_Vulkan_GetInstanceExtensions failed: %s", SDL_GetError());
165
166 std::vector<const char *> extNames(count);
167 if (!SDL_Vulkan_GetInstanceExtensions(window, &count, extNames.data()))
168 throw Exception("SDL_Vulkan_GetInstanceExtensions failed: %s", SDL_GetError());
169
170 vkb::InstanceBuilder builder;
171 builder.require_api_version(1, 0);
172#if !defined(EVENGINE_IOS)
173 // Khronos validation on every draw / descriptor update is typically 5–20×
174 // slower. Opt in with EVENGINE_VULKAN_VALIDATION=1 (any value except "0").
175 const char *vkVal = std::getenv("EVENGINE_VULKAN_VALIDATION");
176 const bool wantValidation = vkVal && vkVal[0] != '\0' && vkVal[0] != '0';
177 if (wantValidation) {
178 builder.request_validation_layers();
179 builder.use_default_debug_messenger();
180 std::printf("EVEngine: Vulkan validation layers enabled (EVENGINE_VULKAN_VALIDATION)\n");
181 }
182#endif
183 for (auto *name : extNames) builder.enable_extension(name);
184#if defined(EVENGINE_MACOSX) || defined(EVENGINE_IOS)
185 // SDL already supplies surface extensions; avoid duplicating them via
186 // InstanceBuilder's non-headless window path, and enable MoltenVK portability.
187 builder.set_headless(true);
188 // Portability enumeration comes from the Khronos loader. iOS links MoltenVK
189 // directly, so the extension is absent there and asking for it aborts
190 // instance creation.
191 if (vkb::SystemInfo::query().is_extension_available(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) {
192 builder.enable_extension(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
193 builder.add_flags(vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR);
194 }
195#endif
196 {
197 StartupStage stage(" vulkan: instance + surface");
198 inst = builder.build();
199
200 VkSurfaceKHR rawSurface = VK_NULL_HANDLE;
201 if (!SDL_Vulkan_CreateSurface(window, static_cast<VkInstance>(inst.instance), &rawSurface))
202 throw Exception("SDL_Vulkan_CreateSurface failed: %s", SDL_GetError());
203 surface = rawSurface;
204 }
205
206 {
207 StartupStage stage(" vulkan: physical device + device");
208 auto selector = inst.selectPhysicalDevice();
209 selector.set_surface(surface).set_minimum_version(1, 0);
210#if defined(EVENGINE_MACOSX) || defined(EVENGINE_IOS)
211 selector.add_required_extension("VK_KHR_portability_subset");
212#endif
213 auto phys = selector.select();
214 {
215 const vk::PhysicalDeviceFeatures supported = phys->getFeatures();
216 if (supported.samplerAnisotropy) {
217 phys.features.samplerAnisotropy = VK_TRUE;
218 maxSamplerAnisotropy = phys.properties.limits.maxSamplerAnisotropy;
219 if (maxSamplerAnisotropy < 1.f) maxSamplerAnisotropy = 1.f;
220 } else {
221 maxSamplerAnisotropy = 1.f;
222 }
223 }
224 vkb::DeviceBuilder deviceBuilder = phys.createDevice();
225 device = deviceBuilder.build();
226 maxSamplerAnisotropy = device.caps.maxSamplerAnisotropy;
227 }
228
229 uploadPool = device.createCommandPool();
230
231 int pw = 0, ph = 0;
232 SDL_Vulkan_GetDrawableSize(window, &pw, &ph);
233 int lw = 0, lh = 0;
234 SDL_GetWindowSize(window, &lw, &lh);
235 setViewportSize(lw, lh, pw, ph);
236
237 {
238 StartupStage stage(" vulkan: swapchain + solid pipelines");
239 createSwapchainAndPipeline();
240 }
241 {
242 StartupStage stage(" vulkan: textured/lit2d pipelines");
243 createTexturedPipeline();
244 }
245 {
246 StartupStage stage(" vulkan: mesh3d pipeline");
247 createMesh3DPipeline();
248 }
249 {
250 StartupStage stage(" vulkan: clustered pipeline");
251 createMesh3DClusteredPipeline();
252 }
253 {
254 StartupStage stage(" vulkan: voxel pipeline");
255 createVoxelRectPipeline();
256 }
257 // Must be set before createShadowResources(): it clears the shadow cascade
258 // layers by calling beginShadowPass()/endShadowPass(), and beginShadowPass()
259 // asserts `initialized` is already true.
260 initialized = true;
261 {
262 StartupStage stage(" vulkan: shadow resources (3x2048^2 maps + pipelines)");
263 createShadowResources();
264 }
265 {
266 StartupStage stage(" vulkan: white texture");
267 const uint8_t whitePixel[4] = {255, 255, 255, 255};
268 whiteTexture = newTexture(1, 1, whitePixel);
269 }
270}
271
273 if (!initialized) return;
274 if (swapchainPassOpen || sceneColorPassOpen) abortOpen3DFrame();
275 // Fire registered window-destroyed callbacks (UI backend tears down its
276 // ImGui context here) before the device/surface resources are released.
277 for (auto &cb : windowDestroyedCallbacks_) cb.first(cb.second);
280
281 device->waitIdle();
282 destroySwapchainResources();
283 swapchain.destroy();
284 swapchain = vkb::Swapchain{};
285 if (surface) {
286 inst.instance.destroySurfaceKHR(surface);
287 surface = VK_NULL_HANDLE;
288 }
289 sdlWindow = nullptr;
290 swapchainDirty = true;
291}
292
293void Graphics::destroySwapchainResources() {
294 presentRecording = {};
295 swapchainPass = {};
296 presentModel.destroy();
297 presentModel = vkb::Present{};
298 depthImage = vkb::DepthStencilImage{};
299 destroyReadbackResources();
300 // Release reused per-frame vertex buffers. Callers hold a device-wide
301 // waitIdle before this runs.
302 for (auto &fb : frame2dBuffers) releaseFrame2dBuffers(fb);
303 frame2dBuffers.clear();
304 releaseFrame2dBuffers(offscreenBuffers);
305 // Per-frame lit-2D UBOs are keyed to the in-flight slot count; release them
306 // here (slot count may change across recreation).
307 for (auto &ubo : lighting2dUboSlots) ubo.release();
308 lighting2dUboSlots.clear();
309 // Descriptor sets cached against the released UBO handles must be dropped;
310 // they live in the shared descriptor pool, so only the cache is cleared.
311 lit2dSets.clear();
312}
313
314uint32_t Graphics::frameSlotCount() const {
315 uint32_t n = presentModel.frames_in_flight;
316 if (n == 0) n = swapchain.image_count;
317 return n ? n : 1u;
318}
319
320size_t Graphics::currentFrameSlot() const {
321 const uint32_t n = frameSlotCount();
322 size_t slot = presentRecording ? presentRecording.slot().index : swapchain.current_frame;
323 return (n > 0 && slot >= n) ? 0 : slot;
324}
325
326vk::CommandBuffer &Graphics::currentPresentCb() {
327 if (offscreen3DPassOpen && offscreen3DCB)
328 return offscreen3DCB;
329 if (swapchainPass)
330 return swapchainPass.commandBuffer();
331 return presentRecording.commandBuffer();
332}
333
334vkb::FrameSlot Graphics::frameToken() const {
335 if (offscreen3DPassOpen) return vkb::FrameSlot{0};
336 if (presentRecording) return presentRecording.slot();
337 return vkb::FrameSlot::gpuIdle();
338}
339
340void Graphics::waitForSharedGpuResources() {
341 presentModel.waitForAllFrames();
342}
343
344void Graphics::invalidateTextureBindings() {
345 for (auto &frame : mesh3dFrameSlots)
346 frame.sets.clear();
347 for (auto &frame : mesh3dClusteredFrameSlots)
348 frame.sets.clear();
349 for (auto &m : lit2dSets) m.clear();
350 offscreenLit2dSets.clear();
351 post2Sets.clear();
352 voxelRectSets.clear();
353}
354
355Graphics::Frame2DBuffers &Graphics::currentFrame2DBuffers() {
356 return currentSlot(frame2dBuffers, frameSlotCount(), currentFrameSlot());
357}
358
359Graphics::Mesh3dFrameSlots &Graphics::currentMesh3dFrameSlots() {
360 return currentSlot(mesh3dFrameSlots, frameSlotCount(), currentFrameSlot());
361}
362
363Graphics::Mesh3dClusteredFrameSlots &Graphics::currentMesh3dClusteredFrameSlots() {
364 return currentSlot(mesh3dClusteredFrameSlots, frameSlotCount(), currentFrameSlot());
365}
366
367vkb::GenericBuffer &Graphics::currentLighting2dUbo() {
368 auto &ubo = currentSlot(lighting2dUboSlots, frameSlotCount(), currentFrameSlot());
369 // allocate() reuses the slot's buffer when it already fits (same usage/
370 // memflags, fixed sizeof(Lighting2DUBO)), so this is a no-op after first use.
371 ubo.allocate(frameToken(), device, vk::BufferUsageFlagBits::eUniformBuffer, sizeof(Lighting2DUBO),
372 vk::MemoryPropertyFlagBits::eHostVisible |
373 vk::MemoryPropertyFlagBits::eHostCoherent);
374 return ubo;
375}
376
377std::unordered_map<Graphics::LitSetKey, vkb::BoundSet, Graphics::LitSetKeyHash> &
378Graphics::currentLit2dSets() {
379 return currentSlot(lit2dSets, frameSlotCount(), currentFrameSlot());
380}
381
382Graphics::ClusteredStorage &Graphics::currentClusteredStorage() {
383 return currentSlot(clusteredStorages, frameSlotCount(), currentFrameSlot());
384}
385
386Graphics::VoxelInstanceFrame &Graphics::currentVoxelInstanceFrame() {
387 return currentSlot(voxelInstanceFrames, frameSlotCount(), currentFrameSlot());
388}
389
390Graphics::ShadowMapSlot &Graphics::currentShadowMap() {
391 ASSERT(!shadowMaps.empty());
392 return shadowMaps[currentFrameSlot() % shadowMaps.size()];
393}
394
395vk::ImageView Graphics::currentShadowArrayView() {
396 if (shadowMaps.empty()) return vk::ImageView{};
397 return currentShadowMap().image.arrayView();
398}
399
400Graphics::GBufferSlot *Graphics::currentGBufferSlot() {
401 if (gbufferSlots.empty()) return nullptr;
402 return &gbufferSlots[currentFrameSlot() % gbufferSlots.size()];
403}
404
405Graphics::SceneColorSlot *Graphics::currentSceneColorSlot() {
406 if (sceneColorSlots.empty()) return nullptr;
407 return &sceneColorSlots[currentFrameSlot() % sceneColorSlots.size()];
408}
409
411 auto *slot = currentSceneColorSlot();
412 if (!slot || !slot->colorTex.gpuHandle) return nullptr;
413 return &slot->colorTex;
414}
415
416Graphics::UiColorSlot *Graphics::currentUiColorSlot() {
417 if (uiColorSlots.empty()) return nullptr;
418 return &uiColorSlots[currentFrameSlot() % uiColorSlots.size()];
419}
420
421void Graphics::dropPendingOffscreenPasses() {
422 shadowPendingMask = 0;
423 for (auto &d : shadowCascadeDraws) d.clear();
424 gbufferPending = false;
425 gbufferPassDraws.clear();
426}
427
428bool Graphics::beginSwapchainRenderPass() {
429 if (!beginPresentCommandBuffer()) {
430 dropPendingOffscreenPasses();
431 return false;
432 }
433 recordDeferredFrameGraph();
434 beginSwapchainColorPass();
435 return true;
436}
437
438void Graphics::beginSwapchainColorPass() {
439 std::array<vk::ClearValue, 2> clears{};
440 clears[0].color = vk::ClearColorValue(
441 std::array<float, 4>{clearColor.r, clearColor.g, clearColor.b, clearColor.a});
442 clears[1].depthStencil = vk::ClearDepthStencilValue{1.0f, 0};
443 swapchainPass = presentRecording.beginRenderPass(renderpass, clears.data(), uint32_t(clears.size()));
444}
445
446bool Graphics::rebuildSwapchainIfNeeded() {
447 const bool wantRecreate =
448 surfaceNeedsRecreate.load() || swapchainDirty || presentModel.needs_recreate;
449 if (!wantRecreate) return true;
450 if (!isRenderSurfaceStable()) return false;
451
452 if (surfaceNeedsRecreate.exchange(false))
453 recreateSurfaceForResume();
454 if (presentModel.needs_recreate) {
455 presentModel.needs_recreate = false;
456 swapchainDirty = true;
457 }
458 if (swapchainDirty) {
459 device->waitIdle();
460 createSwapchainAndPipeline();
461 }
462 return true;
463}
464
465bool Graphics::beginPresentCommandBuffer() {
466 for (int attempt = 0; attempt < 3; ++attempt) {
467 if (!rebuildSwapchainIfNeeded()) return false;
468 presentRecording = presentModel.begin();
469 if (presentRecording) return true;
470 // Acquire failed without beginning the CB (see Present::begin). Rebuild once.
471 if (presentModel.needs_recreate) {
472 presentModel.needs_recreate = false;
473 swapchainDirty = true;
474 continue;
475 }
476 return false;
477 }
478 return false;
479}
480
481bool Graphics::isRenderSurfaceReady() const {
482#if defined(EVENGINE_ANDROID) || defined(EVENGINE_IOS)
483 auto *window = static_cast<SDL_Window *>(sdlWindow);
484 if (!window) return false;
485 // During background/resume and orientation changes the native window is
486 // torn down and rebuilt; SDL reports a zero drawable size until it settles.
487 int pw = 0, ph = 0;
488 SDL_Vulkan_GetDrawableSize(window, &pw, &ph);
489 return pw > 0 && ph > 0;
490#else
491 return true;
492#endif
493}
494
495bool Graphics::isRenderSurfaceStable() {
496#if defined(EVENGINE_ANDROID) || defined(EVENGINE_IOS)
497 auto *window = static_cast<SDL_Window *>(sdlWindow);
498 if (!window) return false;
499 int pw = 0, ph = 0;
500 SDL_Vulkan_GetDrawableSize(window, &pw, &ph);
501 if (pw <= 0 || ph <= 0) {
502 surfaceStableFrames = 0;
503 return false;
504 }
505 if (pw != pendingSurfaceW || ph != pendingSurfaceH) {
506 // Size still changing (mid-rotation / Split View); wait for it to settle.
507 pendingSurfaceW = pw;
508 pendingSurfaceH = ph;
509 surfaceStableFrames = 1;
510 return false;
511 }
512 // Require a couple of consecutive identical polls before rebuilding.
513 if (surfaceStableFrames < 3) {
514 ++surfaceStableFrames;
515 return false;
516 }
517 return true;
518#else
519 return true;
520#endif
521}
522
523void Graphics::recreateSurfaceForResume() {
524 // Mobile platforms destroy the native window when backgrounded, which
525 // invalidates the VkSurfaceKHR. On resume SDL creates a fresh native
526 // window, so we must rebuild the surface (and the swapchain that depends on
527 // it) before presenting again. Runs on the render thread.
528 if (!initialized) return;
529 auto *window = static_cast<SDL_Window *>(sdlWindow);
530 if (!window) return;
531
532 device->waitIdle();
533
534 // Tear down surface-dependent resources first.
535 destroySwapchainResources();
536 swapchain.destroy();
537 swapchain = vkb::Swapchain{};
538 if (surface) {
539 inst.instance.destroySurfaceKHR(surface);
540 surface = VK_NULL_HANDLE;
541 }
542
543 // Create a new surface from the freshly created native window.
544 VkSurfaceKHR rawSurface = VK_NULL_HANDLE;
545 if (!SDL_Vulkan_CreateSurface(window, static_cast<VkInstance>(inst.instance), &rawSurface))
546 throw Exception("resume: SDL_Vulkan_CreateSurface failed: %s", SDL_GetError());
547 surface = rawSurface;
548 device.surface = surface;
549
550 // Refresh drawable size (orientation / Split View may have changed).
551 int pw = 0, ph = 0, lw = 0, lh = 0;
552 SDL_Vulkan_GetDrawableSize(window, &pw, &ph);
553 SDL_GetWindowSize(window, &lw, &lh);
554 if (pw > 0 && ph > 0)
555 setViewportSize(lw, lh, pw, ph);
556
557 swapchainDirty = true;
558}
559
561 if (!initialized) return;
562 if (!isActive()) return;
563 if (isCanvasActive()) throw Exception("present: cannot present while a Canvas is active");
564 if (!isRenderSurfaceReady()) {
565 // Native window is mid-(re)creation / rotation; drop this frame's work
566 // instead of touching an invalid swapchain (which crashes the driver).
567 if (swapchainPassOpen)
568 abortOpen3DFrame();
569 else
570 clear2DBatches();
571 hasPendingClear = false;
572 return;
573 }
574 flushBatch();
575}
576
577void Graphics::draw(eve::graphics::Graphics *, const glm::mat4 &) const {}
578void Graphics::draw(Canvas *, const glm::mat4 &) const {}
579
580
581} // namespace eve::graphics::vulkan
glm::vec3 n
Definition Grass.cpp:64
uint32_t b
const char * name
Definition RockMesh.cpp:21
int d
float m[16]
std::vector< std::pair< WindowDestroyedCallback, void * > > windowDestroyedCallbacks_
Definition Graphics.h:987
std::unique_ptr< AntiAliasing > pipelineAA_
Definition Graphics.h:993
void clearPresentOverlay()
Drop the present overlay callback (window gone; re-register on UI init).
Definition Graphics.h:653
std::unique_ptr< RenderControl > renderControl_
Definition Graphics.h:990
bool isActive() const
Definition Graphics.h:621
std::unique_ptr< GlobalIllumination > pipelineGI_
Definition Graphics.h:992
std::unique_ptr< AmbientOcclusion > pipelineAO_
Definition Graphics.h:991
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
Definition Texture.h:17
void onNativeWindowDestroyed() override
Called by the Window module when the native window backing the render surface is destroyed (window cl...
Definition Graphics.cpp:272
void draw(eve::graphics::Graphics *gfx, const glm::mat4 &matrix) const override
Draws the object with the specified transformation matrix.
Definition Graphics.cpp:577
Texture * getSceneColorTexture() override
Sampleable 3D color target for the current frame (RGB = lit, A = linear depth). Valid after begin3DFr...
Definition Graphics.cpp:410
std::string getBackendName() const override
Renderer backend id used by sibling modules (e.g. Gpgpu).
Definition Graphics.cpp:56
bool isCanvasActive() const override
void initWithWindow(void *nativeWindow) override
Bind to an existing native window (SDL_Window*) and create Vulkan device/swapchain....
Definition Graphics.cpp:139
void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override
Sets the current graphics display viewport dimensions.
Texture * newTexture(int width, int height, const uint8_t *rgba, bool repeatU=false, bool repeatV=false) override
void clear(std::optional< Color > color, std::optional< int > stencil, std::optional< double > depth) override
WidgetDesc window(std::string title, std::vector< WidgetDesc > children, std::string id)
Top-level window widget with a title bar.
Definition Widget.cpp:225