From d5f17eb0ac3008266b30f9999e9efe0b377ac323 Mon Sep 17 00:00:00 2001 From: loki5512344 Date: Mon, 12 Oct 2026 22:10:10 +0200 Subject: [PATCH] refactor(mod): dispatch RenderBackendOps.current() by active backend (Stage 4 kickoff) Stage 4 Vulkan groundwork. Before this, RenderBackendOps.current() unconditionally returned GlRenderBackendOps.INSTANCE with a 'Stage 3-prep' comment. Under Vulkan the GL impl is still harmless (its methods all short-circuit on OptimizeToggles and the GL mixins never fire), but the interface lied about the active backend. - New misc/optimize/vulkan/VulkanRenderBackendOps: singleton, no-op stub, all *Supported() return false, cache/map methods do nothing. This is the skeleton the Stage 4 items (persistent VkPipelineCache, VulkanRenderPass state cache) will grow into. - RenderBackendOps.current() inspects RenderSystem.getDevice().getClass().getName() and returns VulkanRenderBackendOps for com.mojang.blaze3d.vulkan.* devices, GlRenderBackendOps otherwise. Class-name check (not instanceof) so this method itself does not force linking of GlDevice/VulkanDevice. - Wrapped getDevice() in try/catch: unit tests call current() before RenderSystem has a device and previously took the GL path; now they still do via the fallback. Doc: Stage 4 kickoff entry in the changelog. --- mod/docs/optimize-layer-design.md | 6 ++ .../misc/optimize/gl/RenderBackendOps.java | 27 +++++- .../vulkan/VulkanRenderBackendOps.java | 97 +++++++++++++++++++ 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 mod/src/main/java/dev/loki/lovisual/features/module/modules/misc/optimize/vulkan/VulkanRenderBackendOps.java diff --git a/mod/docs/optimize-layer-design.md b/mod/docs/optimize-layer-design.md index f240bf10..56609205 100644 --- a/mod/docs/optimize-layer-design.md +++ b/mod/docs/optimize-layer-design.md @@ -3,6 +3,12 @@ Статус: Этапы 1-3 готовы (2026-10-12). Этап 4 (Vulkan) — стартуем с универсально-полезных пунктов. Цель MC: 26.2. ## История изменений +- **2026-10-12**: Этап 4 старт — `RenderBackendOps.current()` теперь определяет активный бэкенд через + `RenderSystem.getDevice().getClass().getName()` (class-name, не `instanceof`, чтобы не форсировать линковку + `GlDevice`/`VulkanDevice`); добавлен `misc/optimize/vulkan/VulkanRenderBackendOps` — no-op-заглушка со + всеми `*Supported() == false`. Под GL всё работает как раньше; под Vulkan mixins из `mixins/optimize/gl/*` не + загружаются, и теперь хотя бы интерфейс честно возвращает правильный бэкенд. Живые Vulkan-пути + (`VkPipelineCache`, state cache) — следующий шаг. - **2026-10-12**: убран гейт «меряем на Windows до включения». Бенчмарки больше не блокируют работу: универсально-полезные GL/Vulkan-оптимизации делаем сразу за default-off тумблерами (пользователь включает сам), отчёт цифр остаётся как факультативная задача владельца и не входит в план. Этап 3 закрыт по всем пунктам, кроме п.9 (`no_item_sort` / diff --git a/mod/src/main/java/dev/loki/lovisual/features/module/modules/misc/optimize/gl/RenderBackendOps.java b/mod/src/main/java/dev/loki/lovisual/features/module/modules/misc/optimize/gl/RenderBackendOps.java index 00605c8f..3f2feedf 100644 --- a/mod/src/main/java/dev/loki/lovisual/features/module/modules/misc/optimize/gl/RenderBackendOps.java +++ b/mod/src/main/java/dev/loki/lovisual/features/module/modules/misc/optimize/gl/RenderBackendOps.java @@ -1,5 +1,8 @@ package dev.loki.lovisual.features.module.modules.misc.optimize.gl; +import com.mojang.blaze3d.systems.RenderSystem; +import dev.loki.lovisual.features.module.modules.misc.optimize.vulkan.VulkanRenderBackendOps; + /** * Backend-agnostic surface for the optimization layer's GL/Vulkan hooks. * @@ -29,8 +32,30 @@ package dev.loki.lovisual.features.module.modules.misc.optimize.gl; */ public interface RenderBackendOps { - /** Resolve the current backend — GL backend if GL is active, otherwise {@code null}. */ + /** + * Resolve the current backend by inspecting the active {@code GpuDeviceBackend}. + * Class-name check (not {@code instanceof}) so this method itself does not + * force the loader to link {@code GlDevice} or {@code VulkanDevice}; the + * Blaze3D jar always ships both, but keeping the check by string means the + * dispatch stays correct even when only one backend's class has been + * resolved. Falls back to the GL impl when the device is null (early boot) + * so mixins' {@code supported()} short-circuits behave the same as before. + */ static RenderBackendOps current() { + Object device; + try { + device = RenderSystem.getDevice(); + } catch (Throwable ignored) { + // Tests and pre-init paths call current() before RenderSystem has a + // device; fall through to GL so existing unit tests keep passing. + device = null; + } + if (device != null) { + String name = device.getClass().getName(); + if (name.startsWith("com.mojang.blaze3d.vulkan.")) { + return VulkanRenderBackendOps.current(); + } + } return GlRenderBackendOps.current(); } diff --git a/mod/src/main/java/dev/loki/lovisual/features/module/modules/misc/optimize/vulkan/VulkanRenderBackendOps.java b/mod/src/main/java/dev/loki/lovisual/features/module/modules/misc/optimize/vulkan/VulkanRenderBackendOps.java new file mode 100644 index 00000000..fccf117a --- /dev/null +++ b/mod/src/main/java/dev/loki/lovisual/features/module/modules/misc/optimize/vulkan/VulkanRenderBackendOps.java @@ -0,0 +1,97 @@ +package dev.loki.lovisual.features.module.modules.misc.optimize.vulkan; + +import dev.loki.lovisual.features.module.modules.misc.optimize.gl.RenderBackendOps; + +/** + * Vulkan-side implementation of {@link RenderBackendOps}. All GPU-object ops + * (lazy clear, TBO cache, shared depth FBO, RenderTypes cache) currently live + * in the GL mixin set — under Vulkan those mixins never load, so this class is + * a no-op placeholder that lets {@code RenderBackendOps.current()} dispatch by + * actual backend instead of unconditionally returning the GL impl. + * + *

Every {@code *Supported} reports {@code false}. Callers already gate on + * supported before attempting, so the effect is the same as "Optimize GPU + * features off" for backend-dependent items. Stage 4 adds Vulkan-specific + * paths (persistent {@code VkPipelineCache}, {@code VulkanRenderPass} state + * cache) by flipping the corresponding {@code *Supported} here to true and + * implementing the op body. + * + *

Doc reference: {@code mod/docs/optimize-layer-design.md}, "Этап 4" and + * "Что уже решено в архитектуре" → "Рендер-бэкенды". + */ +public final class VulkanRenderBackendOps implements RenderBackendOps { + + private static final VulkanRenderBackendOps INSTANCE = new VulkanRenderBackendOps(); + + private VulkanRenderBackendOps() { + } + + /** Singleton — no state, no per-device instance needed. */ + public static VulkanRenderBackendOps current() { + return INSTANCE; + } + + // --- Lazy clear --- + + @Override + public boolean lazyClearSupported() { + return false; + } + + @Override + public boolean skipBindFramebufferZero(int framebuffer) { + return false; + } + + // --- TBO cache --- + + @Override + public boolean tboCacheSupported() { + return false; + } + + @Override + public void trackBoundTboTexture(int texture) { + } + + @Override + public boolean tboAlreadyAttached(int format, int buffer) { + return false; + } + + @Override + public void markTboAttached(int format, int buffer) { + } + + @Override + public void forgetTboCache() { + } + + // --- FBO share --- + + @Override + public boolean fboShareSupported() { + return false; + } + + @Override + public boolean clearDepthInPlace(int depthId, double value) { + return false; + } + + // --- RenderTypes cache --- + + @Override + public boolean renderTypesCacheSupported() { + return false; + } + + @Override + public Object renderTypeFor(Identifier id) { + return null; + } + + @Override + public void cacheRenderType(Identifier id, Object type) { + } +}