Repository navigation
Vesuvio 0.3.1: engine-side Vulkan speed-ups, memory check - #1
Draft
Atomsk-debug wants to merge 71 commits into
Draft
Atomsk-debug wants to merge 71 commits into
Atomsk-debug wants to merge 71 commits into
Conversation
With cutout leaves on, vanilla draws every leaf face that touches another leaf. New setting leafCulling (Rendering page, live): 0 off, 1 smart (default, drops a face when the next three blocks that way are leaves or opaque), 2 aggressive (two blocks), 3 all. Sections up to three blocks from a changed block are rebuilt so no hole stays. Works for Etna's shadow map too. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
…lder) Options > Video Settings > Vesuvio: Sodium-style pages, options with descriptions, restart markers. F8 (rebindable in Controls) switches all optimizations on/off. Counters on F3 and optionally in the log every 5 s. A benchmark times Vesuvio off/on and each live switch off in turn, writes the table to latest.log and shows a summary. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
Live number settings off the General page now get a step of their own: set to their lowest value (leafCulling 0 = vanilla), or to their default when already at the lowest. Their values are saved at the start and put back with the toggles. Also: "Etna Vesuvio" -> "Vesuvio" in en_us strings and the javadoc, tooltip text updated in en_us/it_it, comments name the real mixins. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
…nchmark LeafCulling kept counters but never registered them with VesuvioStats, so the F3 line, stats log and benchmark counter table showed nothing for it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
Four start-up options on the Memory page, each leaving vanilla bytecode when off: lighter thread checks in chunk section containers, shared identical block shapes (walls, fences, panes), shared per-state face tables, shared namespace strings. Skipped automatically when FerriteCore or ModernFix is installed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
A step that changes how meshes are built (the master switch or leafCulling) rebuilds every loaded section; 3 s is often not enough at high render distance, so the first measured frames included the rebuild. Settling now also waits for the compile queue to empty, up to 30 s. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
Opaque section meshes are sorted into 7 facing groups at compile time; vanilla's main view draws only the groups that can face the camera (one or two draws per section). Etna's shadow pass and other callers keep whole meshes unless they opt in through FaceCulling's API. Settings faceCulling (on) and faceCullingMinGap (64), both live. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
…jfxg1 # Conflicts: # src/main/resources/assets/vesuvio/lang/en_us.json
… builder) A background thread ray-tests candidates through the block grid; entities and block entities whose fresh result is hidden are not extracted. Block entities also get a per-entity frustum test. Particles in sections the occlusion graph can't reach are skipped. Settings entityCulling, blockEntityCulling, particleCulling (all on, live). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
…from the frame builder) animatedTextures: block-atlas sprites not seen for 8 texture ticks are not redrawn (terrain sections, baked quads and sprite UV reads mark them). idleFps/idleFpsLimit: capped frame rate when unfocused, one frame a second when minimized. On macOS, MoltenVK concurrent shader compilation at instance creation (fastStartupCompile, on) and opt-in async submits (off). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
…d hopper lists, path types, POI searches, suffocation (from the ticks builder) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
…wer far re-sorts - chunkScheduler: LevelExtractor's section-update loop queues only as many far rebuilds per frame as keep the workers busy, nearest first; nearby and player-changed sections still go at once; the rest stay dirty for later frames (no more thousands of region copies and queue entries in one frame). - skipEmptySections: all-air sections get the EMPTY mesh directly (no region copy, no task). - trimSectionCopies: SectionCopy keeps only its own section's block entities. - chunkUploadBudget: UberGpuBuffer uploads only what fits in N ms per frame, the rest waits (grows, then a full send after 10 frames); MeshOrder keeps an older build from replacing a newer one. - translucentSortThrottle: far, axis-aligned sections re-sort when the camera moved >= distance/32. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
…installed Fabric API's Indigo renderer replaces vanilla's block renderer during chunk building, so hooks inside ModelBlockRenderer never ran. Leaf culling now hooks Block.shouldRenderFace (called by both renderers) with the block's position recorded in SectionCompiler; animated sprites are taken from each block's model before either renderer draws it. Fix a stale path in CREDITS.md. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
…ns skipped, upload time budget, fewer far re-sorts (from the chunks builder) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
The frame state (camera entity, frustum, view area, occlusion graph) is cleared when LevelExtractor.extract returns, but the per-frame flags stayed set, so a call to the hooked methods outside extract (another mod extracting entities, block entities or particles on its own) would have dereferenced the cleared state. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
…settings box, log and benchmark Registers with VesuvioStats: terrain quads skipped % and face draws added (faces), entities / block entities / particles culled (entities), block shapes shared and memory saved in KB (memory), animated sprites paused (frame). Each reads a plain static, no allocation. F3 now shows the counters three per line so the longer list stays on screen. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
The staging area only starts over after a frame that sent every staged buffer. While the budget holds entries back it keeps filling across frames, and once full every compile worker spins on the dispatcher's lock until the forced frame. Ignore the budget for a frame that finds it more than half full. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
endCompile returned early when the mode was off, so a mode switched off during a compile left the worker thread holding the region (and a stale leaf position for later compiles). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
…ating Only water and lava have vanilla fluid models. With Fabric API a modded fluid is drawn with its render handler's sprites, which the section's sprite list never named, so an animated modded fluid froze a few ticks after its section was built. Such sections are now "unknown". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
Reuse Terrain Draw Lists now builds fresh while Fog Culling cuts sections in the main view: the cut depends on the exact camera position and fog distance, which the draw-list cache key does not hold (standing still while Blindness wore off would otherwise keep the fogged-out sections missing). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
…cannot cut anything; NEEDS gap - FogCulling.beginMainView: when the fog cut-off lies past every section of the view area (render distance plus a margin horizontally, the level's height vertically), the main view does not count as cutting. Rain in the overworld (fog end 768 at full rain) used to make mainViewCutting() true with nothing ever cut, so SectionDrawCache rebuilt the lists every frame. Culling results are unchanged. - VesuvioMixinPlugin: FrameCompiledSectionMeshMixin needs FrameSpriteContentsMixin. AnimatedTextures.markMesh casts a mesh's sprites to SpriteContentsExt, and SectionDrawCache (LevelRendererDrawCacheMixin) calls it without going through FrameLevelRendererMixin's own NEEDS entry. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
…ppedVertexPools) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
…uffersOff) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
…e hidden-window cap Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
…list reuse Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
…rainVertices (off, restart) Half-float positions rounded towards the middle of their 1/16 cell (exact on the 1/16 grid), 16-bit unorm texture coordinates rounded towards the middle of their quad, colour and light unchanged. The format is set in RenderPipelines' terrain snippet at class init, so every chunk pipeline and every copy (Etna's shadow and shadowed terrain pipelines) agrees. BufferBuilder fast and generic paths, the translucent sort and FaceSorter read/write the new format. Float texture coordinates (24 bytes) after a block atlas over 8192 was seen. With the setting off at start-up none of the mixins is applied. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
# Conflicts: # CHANGELOG.md
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
fastEntityModels: each model cube's distinct corners are transformed once (8 instead of 24 transforms per box) through the same Matrix4f call with the same inputs; normals, vertex order and the 11-argument addVertex unchanged, so the bytes are vanilla's bit for bit (checked offline against the game's own Cube.compile). Layout kept on the cube, checked by identity before each use. signTextCache: each sign line is wrapped once per SignText render-messages array; its width and prepared glyph layout (plain and 8x outline) are kept and replayed with the current pose. Keyed on font, position, colour, shadow, include-empty, background, outline colour and a font generation bumped on FontSet reset/close and world change; obfuscated text and non-resource fonts are never cached; LRU bound of 4096 lines. Counters: model cubes fast, sign text cache hits. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
…page) New live setting gpuPassTimers (off by default). On the Vulkan backend each frame in flight gets a timestamp query pool (512 queries, grows to 8192), reset from the CPU (hostQueryReset) right after the backend's own wait for that frame slot and read at the same moment without waiting. vkCmdWriteTimestamp2 (ALL_COMMANDS) right before vkCmdBeginRenderPass2 and right after vkCmdEndRenderPass2, never inside a pass, plus the start and end of every command buffer of the frame (main, object init and transient staging, the last two counted as uploads). Passes are grouped by their label (first 64, then "other"), so Etna's passes show by name. F3 shows the GPU frame time and the five slowest passes (1 s average). The benchmark adds a "GPU pass timers" step (user settings plus the timers, off in every other step; its "vs on" is the cost of measuring) and logs a "GPU passes" table: top 15 passes, other passes, uploads, between passes, ms per frame and share. Unsupported queues (timestampValidBits 0) are detected once; the setting then does nothing and says so. Pools go to the encoder's deferred destroy queue when the setting goes off or the device closes. Off: no query written, no pool. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
The BufferBuilder addVertex/setUv hooks no longer allocate a CallbackInfo per vertex; with compact formats off the BLOCK/ENTITY fast paths carry no Vesuvio hook at all. Written bytes are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
Occlusion and fog tests on the frustum result of entities and particles, and the section fog test, become MEVs: no Operation Object[] or boxing and no LocalRefImpl per entity/particle/section. Fog hooks move to dedicated mixins with explicit priorities so the evaluation order stays occlusion first, then fog (section fog before sprite marking), independent of config registration order. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
Leaf recording becomes a ModifyReceiver on the seed lookup, animated sprite collection MEVs on the model lookup and the fluid test, and face grouping a ModifyReturnValue: no Operation argument arrays, boxed seeds, local wrappers or CallbackInfoReturnable per block or compile. Leaf checks test the block type before reading the volatile mode. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
The face-culling cut is a WrapWithCondition (FaceCulling adds the cut draws itself), skipSamePipeline a WrapWithCondition and skipSameDescriptors an MEV on the dirty test instead of a cancellable HEAD inject. The draw-list cache fills vanilla's list without a throwaway array copy; freed allocations are seen through an MEV. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
The fast model path hooks the cube's face-list read (MEV returning no faces once it wrote the cube) and reuses vanilla's scratch vector; the render thread uses a plain scratch array instead of a ThreadLocal lookup. The suffocation hook is cut down so the JIT can inline it and drop its CIR. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
Resort throttle, transfer barrier hold, kept framebuffer destroy, deferred clears and item frame/sign culling become WrapWithCondition; the staging source check and the chunk scheduler become MEVs, the scheduler working in vanilla's own region cache instead of a second one per frame. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
The saved cache (a few MB) was copied onto LWJGL's 64 KB MemoryStack in PipelineDiskCache.open, throwing OutOfMemoryError (Out of stack space) from the VulkanDevice constructor on every launch after the first. It now goes in memAlloc'd heap memory freed right after vkCreatePipelineCache; any Throwable while loading falls back to no cache; caches over 64 MB are ignored. The instance extension list for the MoltenVK settings also moves off the stack. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Before: Vesuvio 0.1.x trimmed CPU work (culling, chunk scheduling, ticking). With a shader pack the Mac is GPU-bound, so it gained only about 8% (78 to 84 fps with Etna Ultra at 1080p).
After: 0.3.0 also works on the game's native Vulkan renderer. Every change has its own setting, and F8 off gives exact vanilla.
0.2.0 part
0.3.0 part
How: builder reports are in the project files under
etna-designs/boost/report-v020-*.mdandreport-v030-*.md. Runtime audits: 0.2.0 fixed two items (47659e4); 0.3.0 found no medium/high issues; the models/sign audit found none and one cleanup was applied (a4a0bdd); the hook refactor review found no behaviour difference. Test notes:vesuvio-0.3.0/what-to-test.md.Not run in game yet. No release until Cosimo has tested it.
🤖 Generated with Claude Code
https://claude.ai/code/session_01QFKwqp3d86DzZCXDdBVGE5