chore(history): squash 59 commit(s) from 2026-09-23
- refactor(hud/weather/render): Scoreboard 931→240 + Rain 903→159 + UiMeshGeometry 956→99 (8.5.2) - refactor(chams): Chams 985 → 129 + 4×≤195 (8.5.2) - refactor(module): Module 1031 → 139 + 4×≤134 (8.5.2) - refactor(tooltips): BetterTooltips 960 → 181 + 4×≤199 (8.5.2) - refactor(clickgui): ClickGuiPickerState 970→166 + ModulesMenuScreen 966→145 (8.5.2) - refactor(clickgui): CombatProtocolHeuristicsEditorState 932→79 + 4×≤192 (8.5.2) - refactor(hud): CompactHudStatModel 894 → 125 + 4×≤175 (8.5.2) - refactor(iris): ShaderPatchCompiler 843→66 + 4×≤174 (8.5.2) - refactor(tab): CustomTabList 834→136 + 4×≤166 (8.5.2) - refactor(relations): OnlineRelationPlayerPickerComponent 837→74 + 4×≤192 (8.5.2) - refactor(hits): HitEffect 770→120 + 4×≤176 (8.5.2) - refactor(predict): Predictions 815→150 + 4×≤195 (8.5.2) - refactor(config): ConfigProfilesComponent 799→125 + 4×≤195 (8.5.2) - refactor(sim): PlayerMovementSimulation 757→157 + 4×≤194 (8.5.2) - refactor(predict): ProjectilePuncher 747→104 + 3×≤134 (8.5.2) - refactor(crosshair): Crosshair 743→143 + 4×≤167 (8.5.2) - refactor(mediaplayer): MediaPlayer 743→174 + 4×≤196 (8.5.2) - refactor(hud): Itemizer 699→193 + Cooldowns 707→151 (8.5.2) - refactor(hud): Potions 617→176 + 4×≤166 (8.5.2) - refactor(hud): Admins 702→118 + 4×≤138 (8.5.2) - refactor(hud): Keybinds 611→157 + 4×≤160 (8.5.2) - refactor(hud): ModuleList 499→181 + 3×≤150 (8.5.2) - refactor(hud): Radar 392→161 + 2×≤124 (8.5.2) - refactor(hud): Fps 309→168 + 3×≤107 (8.5.2) - refactor(hud): Memory 304→151 + 3×≤144 (8.5.2) - refactor(hud): Tps 339→154 + 3×≤143 (8.5.2) - refactor(hud): Ping 309→145 + 3×≤125 (8.5.2) - refactor(hud): SpeedBps 334→156 + 3×≤142 (8.5.2) - refactor(hud): GameTime 314→150 + 3×≤127 (8.5.2) - refactor(hud): SystemTime 300→136 + 3×≤130 (8.5.2) - refactor(hud): Coordinates 337→174 + 2×≤172 (8.5.2) - refactor(hud): Armor 324→162 + 2×≤148 (8.5.2) - refactor(hud): Inventory 476→153 + 3×≤181 (8.5.2) - docs(todo): обновлён 8.5.2 — отмечены готовые гиганты (BetterButtons/InventorySwap/Svg/Темы/WorldParticles/Module/Chams и хвост) - docs(api): папка combatant-client-26.2/docs + addons.md (API можно ломать при рефакторе) - refactor(visuals): BlockHighlight 692→271 + 4×≤200 (8.5.2) - refactor(mixins): GameRendererMixin 830→344 (хуки) + 4 хелпера (8.5.2) - refactor(visuals): WorldParticles 821→260 + 6×≤200, режимы добиты (8.5.2) - docs(todo): 8.5.2 — WorldParticles/GameRendererMixin/BlockHighlight отмечены ГОТОВО - refactor(render): MeshBuilder 738→447 (фасад write-API) + 3 хелпера, FrameStats снесён (8.5.2) - docs(todo): Фаза 10 — дизайн платформы (аккаунты/конфиги/аватарки), backlog RPC-чата - docs(todo): Подсистема 3 — Аддоны 2.0 (скрипты/версии) + витрина аддонов + админка - docs(todo): Подсистема 2 — RPC-чат/друзья/виджеты-телеметрия + рейт-лимиты, доп. правки Подсистемы 3 - chore: переименование combatant-client-26.2 -> mod, добавлены backend/ и frontend/ - docs(backend): implementation plan for accounts-service (Подсистема 1, часть 1) - chore: пересоздать backend/frontend через cargo new и bun create vite + tailwind; убрать таблицу компонентов из TODO.md - docs(backend): edition 2024 в плане вместо 2021 - chore: обновить версии до реально актуальных (проверено компиляцией) - docs: правила платформы (лимит 250 строк, антипаттерны backend/frontend по итогам ресёрча) + frontend/ARCHITECTURE.md - docs: добавить правило ≤4 файла на папку в правила платформы (backend/frontend) - docs: commit messages in English from now on - chore: gitignore .superpowers/ scratch directory - docs: commit messages in English from now on (mod) - feat(backend): scaffold accounts-service with health check - chore(backend): remove target/ build artifacts from git, add gitignore - chore: track docs/ in git (was accidentally gitignored, never committed) - chore(backend): convert to Cargo workspace, plan full microservice layout - feat(backend): accounts/avatars/device_links schema + migration - feat(backend): argon2id password hashing
This commit is contained in:
parent
ec10395511
commit
9d08fa910a
2239 changed files with 35265 additions and 28110 deletions
|
|
@ -0,0 +1,91 @@
|
|||
package dev.loki.lovisual.mixins;
|
||||
|
||||
import com.mojang.blaze3d.systems.CommandEncoder;
|
||||
import com.mojang.blaze3d.systems.RenderPass;
|
||||
import com.mojang.blaze3d.systems.RenderPassDescriptor;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import dev.loki.lovisual.render.engine.rhi.backend.vulkan.debug.VulkanCrashDiagnostics;
|
||||
|
||||
@Mixin(CommandEncoder.class)
|
||||
public abstract class CommandEncoderDiagnosticsMixin {
|
||||
@Unique
|
||||
private static boolean lovisual$isVulkanBackend() {
|
||||
try {
|
||||
if (RenderSystem.getDevice() == null || RenderSystem.getDevice().getDeviceInfo() == null) return false;
|
||||
String backendName = RenderSystem.getDevice().getDeviceInfo().backendName();
|
||||
return backendName != null && backendName.toLowerCase(java.util.Locale.ROOT).contains("vulkan");
|
||||
} catch (Throwable ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static String lovisual$label(RenderPassDescriptor descriptor) {
|
||||
if (descriptor == null || descriptor.label() == null) return "<null-label>";
|
||||
try {
|
||||
String label = descriptor.label().get();
|
||||
return label == null ? "<null-label>" : label;
|
||||
} catch (Throwable t) {
|
||||
return "<label-error:" + t.getClass().getSimpleName() + ">";
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "createRenderPass(Lcom/mojang/blaze3d/systems/RenderPassDescriptor;)Lcom/mojang/blaze3d/systems/RenderPass;", at = @At("HEAD"))
|
||||
private void lovisual$wrapperCreateHead(RenderPassDescriptor descriptor, CallbackInfoReturnable<RenderPass> cir) {
|
||||
if (!lovisual$isVulkanBackend()) return;
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("commandencoder.wrapper.create.head",
|
||||
"label", lovisual$label(descriptor),
|
||||
"colors", descriptor == null || descriptor.colorAttachments() == null ? -1 : descriptor.colorAttachments().size(),
|
||||
"depth", descriptor != null && descriptor.depthAttachment() != null);
|
||||
}
|
||||
|
||||
@Inject(method = "createRenderPass(Lcom/mojang/blaze3d/systems/RenderPassDescriptor;)Lcom/mojang/blaze3d/systems/RenderPass;", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/CommandEncoderBackend;createRenderPass(Lcom/mojang/blaze3d/systems/RenderPassDescriptor;)Lcom/mojang/blaze3d/systems/RenderPassBackend;", shift = At.Shift.BEFORE))
|
||||
private void lovisual$backendCreateBefore(RenderPassDescriptor descriptor, CallbackInfoReturnable<RenderPass> cir) {
|
||||
if (!lovisual$isVulkanBackend()) return;
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("commandencoder.wrapper.backend.create.before", "label", lovisual$label(descriptor));
|
||||
}
|
||||
|
||||
@Inject(method = "createRenderPass(Lcom/mojang/blaze3d/systems/RenderPassDescriptor;)Lcom/mojang/blaze3d/systems/RenderPass;", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/CommandEncoderBackend;createRenderPass(Lcom/mojang/blaze3d/systems/RenderPassDescriptor;)Lcom/mojang/blaze3d/systems/RenderPassBackend;", shift = At.Shift.AFTER))
|
||||
private void lovisual$backendCreateAfter(RenderPassDescriptor descriptor, CallbackInfoReturnable<RenderPass> cir) {
|
||||
if (!lovisual$isVulkanBackend()) return;
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("commandencoder.wrapper.backend.create.after", "label", lovisual$label(descriptor));
|
||||
}
|
||||
|
||||
@Inject(method = "createRenderPass(Lcom/mojang/blaze3d/systems/RenderPassDescriptor;)Lcom/mojang/blaze3d/systems/RenderPass;", at = @At("RETURN"))
|
||||
private void lovisual$wrapperCreateReturn(RenderPassDescriptor descriptor, CallbackInfoReturnable<RenderPass> cir) {
|
||||
if (!lovisual$isVulkanBackend()) return;
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("commandencoder.wrapper.create.return",
|
||||
"label", lovisual$label(descriptor),
|
||||
"pass", cir.getReturnValue() == null ? "<null>" : cir.getReturnValue().getClass().getName());
|
||||
}
|
||||
|
||||
@Inject(method = "submitRenderPass", at = @At("HEAD"))
|
||||
private void lovisual$wrapperSubmitHead(CallbackInfo ci) {
|
||||
if (!lovisual$isVulkanBackend()) return;
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("commandencoder.wrapper.submit.head");
|
||||
}
|
||||
|
||||
@Inject(method = "submitRenderPass", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/CommandEncoderBackend;submitRenderPass()V", shift = At.Shift.BEFORE))
|
||||
private void lovisual$backendSubmitBefore(CallbackInfo ci) {
|
||||
if (!lovisual$isVulkanBackend()) return;
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("commandencoder.wrapper.backend.submit.before");
|
||||
}
|
||||
|
||||
@Inject(method = "submitRenderPass", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/CommandEncoderBackend;submitRenderPass()V", shift = At.Shift.AFTER))
|
||||
private void lovisual$backendSubmitAfter(CallbackInfo ci) {
|
||||
if (!lovisual$isVulkanBackend()) return;
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("commandencoder.wrapper.backend.submit.after");
|
||||
}
|
||||
|
||||
@Inject(method = "submitRenderPass", at = @At("TAIL"))
|
||||
private void lovisual$wrapperSubmitTail(CallbackInfo ci) {
|
||||
if (!lovisual$isVulkanBackend()) return;
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("commandencoder.wrapper.submit.tail");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
package dev.loki.lovisual.mixins;
|
||||
|
||||
import com.mojang.blaze3d.pipeline.RenderPipeline;
|
||||
import com.mojang.blaze3d.systems.RenderPass;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import dev.loki.lovisual.render.engine.rhi.backend.vulkan.debug.VulkanCrashDiagnostics;
|
||||
|
||||
@Mixin(RenderPass.class)
|
||||
public abstract class RenderPassWrapperDiagnosticsMixin {
|
||||
@Unique
|
||||
private static boolean lovisual$isVulkanBackend() {
|
||||
try {
|
||||
if (RenderSystem.getDevice() == null || RenderSystem.getDevice().getDeviceInfo() == null) return false;
|
||||
String backendName = RenderSystem.getDevice().getDeviceInfo().backendName();
|
||||
return backendName != null && backendName.toLowerCase(java.util.Locale.ROOT).contains("vulkan");
|
||||
} catch (Throwable ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static String lovisual$pipelineId(RenderPipeline pipeline) {
|
||||
if (pipeline == null) return "<null>";
|
||||
try {
|
||||
return String.valueOf(pipeline.getLocation());
|
||||
} catch (Throwable ignored) {
|
||||
return pipeline.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "setPipeline", at = @At("HEAD"))
|
||||
private void lovisual$setPipelineHead(RenderPipeline pipeline, CallbackInfo ci) {
|
||||
if (!lovisual$isVulkanBackend()) return;
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.wrapper.setpipeline.head", "pipeline", lovisual$pipelineId(pipeline));
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "setPipeline",
|
||||
at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderPassBackend;setPipeline(Lcom/mojang/blaze3d/pipeline/RenderPipeline;)V", shift = At.Shift.BEFORE)
|
||||
)
|
||||
private void lovisual$backendSetPipelineBefore(RenderPipeline pipeline, CallbackInfo ci) {
|
||||
if (!lovisual$isVulkanBackend()) return;
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.wrapper.backend.setpipeline.before", "pipeline", lovisual$pipelineId(pipeline));
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "setPipeline",
|
||||
at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderPassBackend;setPipeline(Lcom/mojang/blaze3d/pipeline/RenderPipeline;)V", shift = At.Shift.AFTER)
|
||||
)
|
||||
private void lovisual$backendSetPipelineAfter(RenderPipeline pipeline, CallbackInfo ci) {
|
||||
if (!lovisual$isVulkanBackend()) return;
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.wrapper.backend.setpipeline.after", "pipeline", lovisual$pipelineId(pipeline));
|
||||
}
|
||||
|
||||
@Inject(method = "close", at = @At("HEAD"))
|
||||
private void lovisual$closeHead(CallbackInfo ci) {
|
||||
if (!lovisual$isVulkanBackend()) return;
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.wrapper.close.head");
|
||||
}
|
||||
|
||||
@Inject(method = "close", at = @At(value = "INVOKE", target = "Ljava/lang/Runnable;run()V", shift = At.Shift.BEFORE))
|
||||
private void lovisual$closeOnFinishBefore(CallbackInfo ci) {
|
||||
if (!lovisual$isVulkanBackend()) return;
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.wrapper.close.onfinish.before");
|
||||
}
|
||||
|
||||
@Inject(method = "close", at = @At(value = "INVOKE", target = "Ljava/lang/Runnable;run()V", shift = At.Shift.AFTER))
|
||||
private void lovisual$closeOnFinishAfter(CallbackInfo ci) {
|
||||
if (!lovisual$isVulkanBackend()) return;
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.wrapper.close.onfinish.after");
|
||||
}
|
||||
|
||||
@Inject(method = "close", at = @At("TAIL"))
|
||||
private void lovisual$closeTail(CallbackInfo ci) {
|
||||
if (!lovisual$isVulkanBackend()) return;
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.wrapper.close.tail");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package dev.loki.lovisual.mixins;
|
||||
|
||||
import com.mojang.blaze3d.shaders.GpuDebugOptions;
|
||||
import com.mojang.blaze3d.vulkan.VulkanBackend;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import dev.loki.lovisual.render.engine.rhi.backend.vulkan.debug.VulkanCrashDiagnostics;
|
||||
|
||||
@Mixin(VulkanBackend.class)
|
||||
public abstract class VulkanBackendMixin {
|
||||
@ModifyVariable(method = "createDevice", at = @At("HEAD"), argsOnly = true)
|
||||
private GpuDebugOptions lovisual$forceDebugOptions(GpuDebugOptions options) {
|
||||
return VulkanCrashDiagnostics.forceDebugOptions(options);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package dev.loki.lovisual.mixins;
|
||||
|
||||
import com.mojang.blaze3d.vulkan.checkpoints.CheckpointExtension;
|
||||
import org.lwjgl.vulkan.VkCommandBuffer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import dev.loki.lovisual.render.engine.rhi.backend.vulkan.debug.VulkanCrashDiagnostics;
|
||||
import dev.loki.lovisual.render.engine.rhi.backend.vulkan.util.VulkanRenderStateBridge;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Mixin(targets = "com.mojang.blaze3d.vulkan.checkpoints.AbstractCheckpointStorage")
|
||||
public abstract class VulkanCheckpointDiagnosticsMixin {
|
||||
@Inject(method = "recordCheckpoint(Lorg/lwjgl/vulkan/VkCommandBuffer;Lcom/mojang/blaze3d/vulkan/checkpoints/CheckpointExtension$CheckpointType;Ljava/util/function/Supplier;)V", at = @At("HEAD"))
|
||||
private void lovisual$logCheckpoint(VkCommandBuffer commandBuffer,
|
||||
CheckpointExtension.CheckpointType type,
|
||||
Supplier<String> label,
|
||||
CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet(VulkanRenderStateBridge.forceDisableCheckpoints() ? "checkpoint.disabled" : "checkpoint.record",
|
||||
"type", type,
|
||||
"label", safeLabel(label),
|
||||
"commandBuffer", commandBuffer == null ? 0L : commandBuffer.address());
|
||||
}
|
||||
|
||||
private static String safeLabel(Supplier<String> label) {
|
||||
if (label == null) return "<null>";
|
||||
try {
|
||||
return label.get();
|
||||
} catch (Throwable t) {
|
||||
return "<label-error:" + VulkanCrashDiagnostics.throwable(t) + ">";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
package dev.loki.lovisual.mixins;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.mojang.blaze3d.systems.RenderPassBackend;
|
||||
import com.mojang.blaze3d.systems.RenderPassDescriptor;
|
||||
import com.mojang.blaze3d.vulkan.VulkanCommandEncoder;
|
||||
import org.lwjgl.vulkan.VkRenderingAttachmentInfo;
|
||||
import org.lwjgl.vulkan.VkRenderingInfo;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import dev.loki.lovisual.render.engine.rhi.backend.vulkan.debug.VulkanCrashDiagnostics;
|
||||
import dev.loki.lovisual.render.engine.rhi.backend.vulkan.util.VulkanRenderStateBridge;
|
||||
|
||||
@Mixin(VulkanCommandEncoder.class)
|
||||
public abstract class VulkanCommandEncoderDiagnosticsMixin {
|
||||
@Inject(method = "createRenderPass", at = @At("HEAD"))
|
||||
private void lovisual$renderPassCreateHead(RenderPassDescriptor descriptor, CallbackInfoReturnable<RenderPassBackend> cir) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.create.head",
|
||||
"colors", descriptor == null || descriptor.colorAttachments() == null ? -1 : descriptor.colorAttachments().size(),
|
||||
"depth", descriptor != null && descriptor.depthAttachment() != null,
|
||||
"samples", VulkanRenderStateBridge.currentRenderPassSamples(),
|
||||
"stencil", VulkanRenderStateBridge.currentRenderPassNeedsStencilAttachment());
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "createRenderPass",
|
||||
at = @At(value = "INVOKE", target = "Lorg/lwjgl/vulkan/KHRDynamicRendering;vkCmdBeginRenderingKHR(Lorg/lwjgl/vulkan/VkCommandBuffer;Lorg/lwjgl/vulkan/VkRenderingInfo;)V", shift = At.Shift.BEFORE)
|
||||
)
|
||||
private void lovisual$beforeBeginRendering(RenderPassDescriptor descriptor,
|
||||
CallbackInfoReturnable<RenderPassBackend> cir,
|
||||
@Local VkRenderingInfo renderingInfo) {
|
||||
int colorResolves = 0;
|
||||
VkRenderingAttachmentInfo.Buffer colors = renderingInfo == null ? null : renderingInfo.pColorAttachments();
|
||||
if (colors != null) {
|
||||
for (int i = 0; i < colors.remaining(); i++) {
|
||||
if (colors.get(i).resolveImageView() != 0L) colorResolves++;
|
||||
}
|
||||
}
|
||||
VkRenderingAttachmentInfo depth = renderingInfo == null ? null : renderingInfo.pDepthAttachment();
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.beginrendering.before",
|
||||
"colors", descriptor == null || descriptor.colorAttachments() == null ? -1 : descriptor.colorAttachments().size(),
|
||||
"depth", descriptor != null && descriptor.depthAttachment() != null,
|
||||
"samples", VulkanRenderStateBridge.currentRenderPassSamples(),
|
||||
"stencil", VulkanRenderStateBridge.currentRenderPassNeedsStencilAttachment(),
|
||||
"colorResolves", colorResolves,
|
||||
"depthResolveMode", depth == null ? 0 : depth.resolveMode(),
|
||||
"depthResolveView", depth == null ? 0L : depth.resolveImageView(),
|
||||
"renderingInfo", renderingInfo == null ? 0L : renderingInfo.address());
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "createRenderPass",
|
||||
at = @At(value = "INVOKE", target = "Lorg/lwjgl/vulkan/KHRDynamicRendering;vkCmdBeginRenderingKHR(Lorg/lwjgl/vulkan/VkCommandBuffer;Lorg/lwjgl/vulkan/VkRenderingInfo;)V", shift = At.Shift.AFTER)
|
||||
)
|
||||
private void lovisual$afterBeginRendering(RenderPassDescriptor descriptor,
|
||||
CallbackInfoReturnable<RenderPassBackend> cir) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.beginrendering.after");
|
||||
}
|
||||
|
||||
@Inject(method = "createRenderPass", at = @At("RETURN"))
|
||||
private void lovisual$renderPassCreated(RenderPassDescriptor descriptor, CallbackInfoReturnable<RenderPassBackend> cir) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.create.return",
|
||||
"backend", cir.getReturnValue() == null ? "<null>" : cir.getReturnValue().getClass().getName());
|
||||
}
|
||||
|
||||
@Inject(method = "submitRenderPass", at = @At("HEAD"))
|
||||
private void lovisual$submitVulkanRenderPassHead(CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.submit.head");
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "submitRenderPass",
|
||||
at = @At(value = "INVOKE", target = "Lorg/lwjgl/vulkan/KHRDynamicRendering;vkCmdEndRenderingKHR(Lorg/lwjgl/vulkan/VkCommandBuffer;)V", shift = At.Shift.BEFORE)
|
||||
)
|
||||
private void lovisual$beforeEndRendering(CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.endrendering.before");
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "submitRenderPass",
|
||||
at = @At(value = "INVOKE", target = "Lorg/lwjgl/vulkan/KHRDynamicRendering;vkCmdEndRenderingKHR(Lorg/lwjgl/vulkan/VkCommandBuffer;)V", shift = At.Shift.AFTER)
|
||||
)
|
||||
private void lovisual$afterEndRendering(CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.endrendering.after");
|
||||
}
|
||||
|
||||
@Inject(method = "submitRenderPass", at = @At("TAIL"))
|
||||
private void lovisual$submitVulkanRenderPassTail(CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.submit.tail");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package dev.loki.lovisual.mixins;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import dev.loki.lovisual.render.engine.rhi.backend.vulkan.debug.VulkanCrashDiagnostics;
|
||||
|
||||
@Mixin(targets = "com.mojang.blaze3d.vulkan.VulkanDebug$Enabled")
|
||||
public abstract class VulkanDebugMessageMixin {
|
||||
@Inject(method = "onDebugMessage", at = @At("HEAD"))
|
||||
private void lovisual$captureDriverDebugMessage(int severity,
|
||||
int type,
|
||||
long callbackData,
|
||||
long userData,
|
||||
CallbackInfoReturnable<Integer> cir) {
|
||||
VulkanCrashDiagnostics.driverDebugMessage(severity, type, callbackData);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package dev.loki.lovisual.mixins;
|
||||
|
||||
import com.mojang.blaze3d.shaders.ShaderSource;
|
||||
import com.mojang.blaze3d.vulkan.VulkanDevice;
|
||||
import com.mojang.blaze3d.vulkan.VulkanInstance;
|
||||
import com.mojang.blaze3d.vulkan.VulkanPhysicalDevice;
|
||||
import com.mojang.blaze3d.vulkan.checkpoints.CheckpointExtension;
|
||||
import org.lwjgl.vulkan.VkDevice;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import dev.loki.lovisual.render.engine.rhi.backend.vulkan.debug.VulkanCrashDiagnostics;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Mixin(VulkanDevice.class)
|
||||
public abstract class VulkanDeviceDiagnosticsMixin {
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void lovisual$dumpVulkanDevice(ShaderSource shaderSource,
|
||||
VulkanInstance instance,
|
||||
VulkanPhysicalDevice physicalDevice,
|
||||
Set<String> enabledExtensions,
|
||||
VkDevice device,
|
||||
long vma,
|
||||
CheckpointExtension checkpointExtension,
|
||||
CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.dumpDevice(instance, physicalDevice, enabledExtensions);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package dev.loki.lovisual.mixins;
|
||||
|
||||
import org.lwjgl.vulkan.KHRSynchronization2;
|
||||
import org.lwjgl.vulkan.VkQueue;
|
||||
import org.lwjgl.vulkan.VkSubmitInfo2;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import dev.loki.lovisual.render.engine.rhi.backend.vulkan.debug.VulkanCrashDiagnostics;
|
||||
|
||||
@Mixin(targets = "com.mojang.blaze3d.vulkan.VulkanQueue$Submission")
|
||||
public abstract class VulkanQueueSubmissionMixin {
|
||||
@Redirect(
|
||||
method = "close",
|
||||
at = @At(value = "INVOKE", target = "Lorg/lwjgl/vulkan/KHRSynchronization2;vkQueueSubmit2KHR(Lorg/lwjgl/vulkan/VkQueue;Lorg/lwjgl/vulkan/VkSubmitInfo2$Buffer;J)I")
|
||||
)
|
||||
private int lovisual$diagnoseQueueSubmit(VkQueue queue, VkSubmitInfo2.Buffer submits, long fence) {
|
||||
int submitCount = submits == null ? -1 : submits.remaining();
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("queue.submit.before", "submits", submitCount, "fence", fence);
|
||||
int result = KHRSynchronization2.vkQueueSubmit2KHR(queue, submits, fence);
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("queue.submit.after", "result", result, "submits", submitCount, "fence", fence);
|
||||
if (result != 0) {
|
||||
VulkanCrashDiagnostics.breadcrumb("queue.submit.failed", "result", result, "submits", submitCount, "fence", fence);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,288 @@
|
|||
package dev.loki.lovisual.mixins;
|
||||
|
||||
import com.mojang.blaze3d.IndexType;
|
||||
import com.mojang.blaze3d.buffers.GpuBuffer;
|
||||
import com.mojang.blaze3d.buffers.GpuBufferSlice;
|
||||
import com.mojang.blaze3d.pipeline.RenderPipeline;
|
||||
import com.mojang.blaze3d.systems.RenderPass;
|
||||
import com.mojang.blaze3d.textures.GpuSampler;
|
||||
import com.mojang.blaze3d.textures.GpuTextureView;
|
||||
import com.mojang.blaze3d.vulkan.VulkanCommandEncoder;
|
||||
import com.mojang.blaze3d.vulkan.VulkanDevice;
|
||||
import com.mojang.blaze3d.vulkan.VulkanRenderPass;
|
||||
import com.mojang.blaze3d.vulkan.checkpoints.CheckpointExtension;
|
||||
import org.lwjgl.PointerBuffer;
|
||||
import org.lwjgl.vulkan.VkCommandBuffer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import dev.loki.lovisual.render.engine.rhi.backend.vulkan.debug.VulkanCrashDiagnostics;
|
||||
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Mixin(VulkanRenderPass.class)
|
||||
public abstract class VulkanRenderPassDiagnosticsMixin {
|
||||
|
||||
@Inject(method = "<init>", at = @At("HEAD"))
|
||||
private static void lovisual$ctorHead(VulkanDevice device,
|
||||
VulkanCommandEncoder encoder,
|
||||
VkCommandBuffer commandBuffer,
|
||||
CheckpointExtension.CheckpointStorage checkpointStorage,
|
||||
RenderPass.RenderArea renderArea,
|
||||
int outputWidth,
|
||||
int outputHeight,
|
||||
boolean hasDepth,
|
||||
Supplier<String> label,
|
||||
CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.object.ctor.head",
|
||||
"label", labelText(label),
|
||||
"area", areaText(renderArea),
|
||||
"output", outputWidth + "x" + outputHeight,
|
||||
"depth", hasDepth,
|
||||
"commandBuffer", commandBuffer == null ? 0L : commandBuffer.address());
|
||||
}
|
||||
|
||||
@Inject(method = "<init>", at = @At("TAIL"))
|
||||
private void lovisual$ctorTail(VulkanDevice device,
|
||||
VulkanCommandEncoder encoder,
|
||||
VkCommandBuffer commandBuffer,
|
||||
CheckpointExtension.CheckpointStorage checkpointStorage,
|
||||
RenderPass.RenderArea renderArea,
|
||||
int outputWidth,
|
||||
int outputHeight,
|
||||
boolean hasDepth,
|
||||
Supplier<String> label,
|
||||
CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.object.ctor.tail",
|
||||
"label", labelText(label),
|
||||
"area", areaText(renderArea));
|
||||
}
|
||||
|
||||
@Inject(method = "setPipeline", at = @At("HEAD"))
|
||||
private void lovisual$setPipelineHead(RenderPipeline pipeline, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.setpipeline.head", "pipeline", pipelineId(pipeline));
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "setPipeline",
|
||||
at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vulkan/VulkanDevice;getOrCompilePipeline(Lcom/mojang/blaze3d/pipeline/RenderPipeline;)Lcom/mojang/blaze3d/vulkan/VulkanRenderPipeline;", shift = At.Shift.BEFORE)
|
||||
)
|
||||
private void lovisual$compilePipelineBefore(RenderPipeline pipeline, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.pipeline.get.before", "pipeline", pipelineId(pipeline));
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "setPipeline",
|
||||
at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vulkan/VulkanDevice;getOrCompilePipeline(Lcom/mojang/blaze3d/pipeline/RenderPipeline;)Lcom/mojang/blaze3d/vulkan/VulkanRenderPipeline;", shift = At.Shift.AFTER)
|
||||
)
|
||||
private void lovisual$compilePipelineAfter(RenderPipeline pipeline, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.pipeline.get.after", "pipeline", pipelineId(pipeline));
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "setPipeline",
|
||||
at = @At(value = "INVOKE", target = "Lorg/lwjgl/vulkan/VK12;vkCmdBindPipeline(Lorg/lwjgl/vulkan/VkCommandBuffer;IJ)V", shift = At.Shift.BEFORE)
|
||||
)
|
||||
private void lovisual$bindPipelineBefore(RenderPipeline pipeline, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.bindpipeline.before", "pipeline", pipelineId(pipeline));
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "setPipeline",
|
||||
at = @At(value = "INVOKE", target = "Lorg/lwjgl/vulkan/VK12;vkCmdBindPipeline(Lorg/lwjgl/vulkan/VkCommandBuffer;IJ)V", shift = At.Shift.AFTER)
|
||||
)
|
||||
private void lovisual$bindPipelineAfter(RenderPipeline pipeline, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.bindpipeline.after", "pipeline", pipelineId(pipeline));
|
||||
}
|
||||
|
||||
@Inject(method = "setPipeline", at = @At("TAIL"))
|
||||
private void lovisual$setPipelineTail(RenderPipeline pipeline, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.setpipeline.tail", "pipeline", pipelineId(pipeline));
|
||||
}
|
||||
|
||||
@Inject(method = "bindTexture", at = @At("HEAD"))
|
||||
private void lovisual$bindTextureHead(String name, GpuTextureView view, GpuSampler sampler, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.bindtexture.head", "name", name, "view", objectText(view), "sampler", objectText(sampler));
|
||||
}
|
||||
|
||||
@Inject(method = "setUniform(Ljava/lang/String;Lcom/mojang/blaze3d/buffers/GpuBufferSlice;)V", at = @At("HEAD"))
|
||||
private void lovisual$setUniformSliceHead(String name, GpuBufferSlice slice, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.uniform.slice.head", "name", name, "slice", sliceText(slice));
|
||||
}
|
||||
|
||||
@Inject(method = "setUniform(Ljava/lang/String;Lcom/mojang/blaze3d/buffers/GpuBuffer;)V", at = @At("HEAD"))
|
||||
private void lovisual$setUniformBufferHead(String name, GpuBuffer buffer, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.uniform.buffer.head", "name", name, "buffer", bufferText(buffer));
|
||||
}
|
||||
|
||||
@Inject(method = "enableScissor", at = @At("HEAD"))
|
||||
private void lovisual$enableScissorHead(int x, int y, int width, int height, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.scissor.enable.head", "rect", x + "," + y + " " + width + "x" + height);
|
||||
}
|
||||
|
||||
@Inject(method = "setVertexBuffer", at = @At("HEAD"))
|
||||
private void lovisual$vertexBufferHead(int slot, GpuBufferSlice slice, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.vertexbuffer.head", "slot", slot, "slice", sliceText(slice));
|
||||
}
|
||||
|
||||
@Inject(method = "setIndexBuffer", at = @At("HEAD"))
|
||||
private void lovisual$indexBufferHead(GpuBuffer buffer, IndexType indexType, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.indexbuffer.head", "buffer", bufferText(buffer), "type", indexType);
|
||||
}
|
||||
|
||||
@Inject(method = "pushDescriptors", at = @At("HEAD"))
|
||||
private void lovisual$pushDescriptorsHead(CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.pushdescriptors.head");
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "pushDescriptors",
|
||||
at = @At(value = "INVOKE", target = "Lorg/lwjgl/vulkan/KHRPushDescriptor;vkCmdPushDescriptorSetKHR(Lorg/lwjgl/vulkan/VkCommandBuffer;IJILorg/lwjgl/vulkan/VkWriteDescriptorSet$Buffer;)V", shift = At.Shift.BEFORE)
|
||||
)
|
||||
private void lovisual$pushDescriptorsNativeBefore(CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.pushdescriptors.native.before");
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "pushDescriptors",
|
||||
at = @At(value = "INVOKE", target = "Lorg/lwjgl/vulkan/KHRPushDescriptor;vkCmdPushDescriptorSetKHR(Lorg/lwjgl/vulkan/VkCommandBuffer;IJILorg/lwjgl/vulkan/VkWriteDescriptorSet$Buffer;)V", shift = At.Shift.AFTER)
|
||||
)
|
||||
private void lovisual$pushDescriptorsNativeAfter(CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.pushdescriptors.native.after");
|
||||
}
|
||||
|
||||
@Inject(method = "pushDescriptors", at = @At("TAIL"))
|
||||
private void lovisual$pushDescriptorsTail(CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.pushdescriptors.tail");
|
||||
}
|
||||
|
||||
@Inject(method = "drawIndexed", at = @At("HEAD"))
|
||||
private void lovisual$drawIndexedHead(int indexCount, int instanceCount, int firstIndex, int vertexOffset, int firstInstance, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbDraw("renderpass.drawindexed.head",
|
||||
"indices", indexCount,
|
||||
"instances", instanceCount,
|
||||
"firstIndex", firstIndex,
|
||||
"vertexOffset", vertexOffset,
|
||||
"firstInstance", firstInstance);
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "drawIndexed",
|
||||
at = @At(value = "INVOKE", target = "Lorg/lwjgl/vulkan/VK12;vkCmdDrawIndexed(Lorg/lwjgl/vulkan/VkCommandBuffer;IIIII)V", shift = At.Shift.BEFORE)
|
||||
)
|
||||
private void lovisual$drawIndexedNativeBefore(int indexCount, int instanceCount, int firstIndex, int vertexOffset, int firstInstance, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbDraw("renderpass.drawindexed.native.before", "indices", indexCount);
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "drawIndexed",
|
||||
at = @At(value = "INVOKE", target = "Lorg/lwjgl/vulkan/VK12;vkCmdDrawIndexed(Lorg/lwjgl/vulkan/VkCommandBuffer;IIIII)V", shift = At.Shift.AFTER)
|
||||
)
|
||||
private void lovisual$drawIndexedNativeAfter(int indexCount, int instanceCount, int firstIndex, int vertexOffset, int firstInstance, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbDraw("renderpass.drawindexed.native.after", "indices", indexCount);
|
||||
}
|
||||
|
||||
@Inject(method = "draw", at = @At("HEAD"))
|
||||
private void lovisual$drawHead(int vertexCount, int instanceCount, int firstVertex, int firstInstance, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbDraw("renderpass.draw.head",
|
||||
"vertices", vertexCount,
|
||||
"instances", instanceCount,
|
||||
"firstVertex", firstVertex,
|
||||
"firstInstance", firstInstance);
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "draw",
|
||||
at = @At(value = "INVOKE", target = "Lorg/lwjgl/vulkan/VK12;vkCmdDraw(Lorg/lwjgl/vulkan/VkCommandBuffer;IIII)V", shift = At.Shift.BEFORE)
|
||||
)
|
||||
private void lovisual$drawNativeBefore(int vertexCount, int instanceCount, int firstVertex, int firstInstance, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbDraw("renderpass.draw.native.before", "vertices", vertexCount);
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "draw",
|
||||
at = @At(value = "INVOKE", target = "Lorg/lwjgl/vulkan/VK12;vkCmdDraw(Lorg/lwjgl/vulkan/VkCommandBuffer;IIII)V", shift = At.Shift.AFTER)
|
||||
)
|
||||
private void lovisual$drawNativeAfter(int vertexCount, int instanceCount, int firstVertex, int firstInstance, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbDraw("renderpass.draw.native.after", "vertices", vertexCount);
|
||||
}
|
||||
|
||||
@Inject(method = "multiDraw(Ljava/nio/IntBuffer;III)V", at = @At("HEAD"))
|
||||
private void lovisual$multiDrawHead(IntBuffer buffer, int firstInstance, int drawCount, int stride, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbDraw("renderpass.multidraw.head", "draws", drawCount, "stride", stride, "remaining", buffer == null ? -1 : buffer.remaining());
|
||||
}
|
||||
|
||||
@Inject(method = "multiDrawIndexed(Ljava/nio/IntBuffer;III)V", at = @At("HEAD"))
|
||||
private void lovisual$multiDrawIndexedHead(IntBuffer buffer, int firstInstance, int drawCount, int stride, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbDraw("renderpass.multidrawindexed.head", "draws", drawCount, "stride", stride, "remaining", buffer == null ? -1 : buffer.remaining());
|
||||
}
|
||||
|
||||
@Inject(method = "multiDrawIndexed(Lorg/lwjgl/PointerBuffer;Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;I)V", at = @At("HEAD"))
|
||||
private void lovisual$multiDrawIndexedPointerHead(PointerBuffer firstIndex, IntBuffer indexCount, IntBuffer baseVertex, int drawCount, CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbDraw("renderpass.multidrawindexed.pointer.head", "draws", drawCount);
|
||||
}
|
||||
|
||||
@Inject(method = "drawMultipleIndexed", at = @At("HEAD"))
|
||||
private <T> void lovisual$drawMultipleIndexedHead(Collection<RenderPass.Draw<T>> draws,
|
||||
GpuBuffer indexBuffer,
|
||||
IndexType indexType,
|
||||
Collection<String> dynamicUniforms,
|
||||
T extraData,
|
||||
CallbackInfo ci) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("renderpass.drawmultipleindexed.head",
|
||||
"draws", draws == null ? -1 : draws.size(),
|
||||
"indexBuffer", bufferText(indexBuffer),
|
||||
"indexType", indexType,
|
||||
"dynamicUniforms", dynamicUniforms == null ? -1 : dynamicUniforms.size());
|
||||
}
|
||||
|
||||
private static String pipelineId(RenderPipeline pipeline) {
|
||||
if (pipeline == null) return "<null>";
|
||||
try {
|
||||
return String.valueOf(pipeline.getLocation());
|
||||
} catch (Throwable ignored) {
|
||||
return pipeline.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static String labelText(Supplier<String> label) {
|
||||
if (label == null) return "<null>";
|
||||
try {
|
||||
String value = label.get();
|
||||
return value == null ? "<null>" : value;
|
||||
} catch (Throwable t) {
|
||||
return "<label-error:" + t.getClass().getSimpleName() + ">";
|
||||
}
|
||||
}
|
||||
|
||||
private static String areaText(RenderPass.RenderArea area) {
|
||||
if (area == null) return "<null>";
|
||||
return area.x() + "," + area.y() + " " + area.width() + "x" + area.height();
|
||||
}
|
||||
|
||||
private static String sliceText(GpuBufferSlice slice) {
|
||||
if (slice == null) return "<null>";
|
||||
try {
|
||||
return "offset=" + slice.offset() + " buffer=" + bufferText(slice.buffer());
|
||||
} catch (Throwable t) {
|
||||
return "<slice-error:" + t.getClass().getSimpleName() + ">";
|
||||
}
|
||||
}
|
||||
|
||||
private static String bufferText(GpuBuffer buffer) {
|
||||
if (buffer == null) return "<null>";
|
||||
try {
|
||||
return buffer.getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(buffer)) + " closed=" + buffer.isClosed();
|
||||
} catch (Throwable t) {
|
||||
return buffer.getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(buffer)) + " error=" + t.getClass().getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static String objectText(Object object) {
|
||||
if (object == null) return "<null>";
|
||||
return object.getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(object));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package dev.loki.lovisual.mixins;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.mojang.blaze3d.pipeline.RenderPipeline;
|
||||
import com.mojang.blaze3d.vulkan.VulkanBindGroupLayout;
|
||||
import com.mojang.blaze3d.vulkan.VulkanDevice;
|
||||
import com.mojang.blaze3d.vulkan.VulkanRenderPipeline;
|
||||
import org.lwjgl.vulkan.VkPipelineDepthStencilStateCreateInfo;
|
||||
import org.lwjgl.vulkan.VkPipelineRenderingCreateInfoKHR;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import dev.loki.lovisual.render.engine.rhi.backend.vulkan.debug.VulkanCrashDiagnostics;
|
||||
import dev.loki.lovisual.render.engine.rhi.backend.vulkan.util.VulkanRenderStateBridge;
|
||||
|
||||
@Mixin(VulkanRenderPipeline.class)
|
||||
public abstract class VulkanRenderPipelineDiagnosticsMixin {
|
||||
@Inject(
|
||||
method = "compile",
|
||||
at = @At(value = "INVOKE", target = "Lorg/lwjgl/vulkan/VK12;vkCreateGraphicsPipelines(Lorg/lwjgl/vulkan/VkDevice;JLorg/lwjgl/vulkan/VkGraphicsPipelineCreateInfo$Buffer;Lorg/lwjgl/vulkan/VkAllocationCallbacks;Ljava/nio/LongBuffer;)I", ordinal = 0, shift = At.Shift.BEFORE)
|
||||
)
|
||||
private static void lovisual$logPipelineCompile(VulkanDevice device,
|
||||
VulkanBindGroupLayout layout,
|
||||
RenderPipeline pipeline,
|
||||
long vertexModule,
|
||||
long fragmentModule,
|
||||
CallbackInfoReturnable<VulkanRenderPipeline> cir,
|
||||
@Local VkPipelineRenderingCreateInfoKHR rendering,
|
||||
@Local VkPipelineDepthStencilStateCreateInfo depthStencil) {
|
||||
VulkanCrashDiagnostics.breadcrumbQuiet("pipeline.compile.before",
|
||||
"pipeline", pipeline == null || pipeline.getLocation() == null ? "<unknown>" : pipeline.getLocation(),
|
||||
"samples", VulkanRenderStateBridge.currentVkSampleCount(),
|
||||
"stencil", VulkanRenderStateBridge.pipelineNeedsStencilFormat(pipeline),
|
||||
"stencilMode", VulkanRenderStateBridge.stencilMode());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,326 @@
|
|||
package dev.loki.lovisual.render.engine.rhi.backend.vulkan.debug;
|
||||
|
||||
import com.mojang.blaze3d.shaders.GpuDebugOptions;
|
||||
import com.mojang.blaze3d.vulkan.VulkanInstance;
|
||||
import com.mojang.blaze3d.vulkan.VulkanPhysicalDevice;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import org.lwjgl.vulkan.VK12;
|
||||
import org.lwjgl.vulkan.VkDebugUtilsMessengerCallbackDataEXT;
|
||||
import org.lwjgl.vulkan.VkPhysicalDeviceLimits;
|
||||
import org.lwjgl.vulkan.VkPhysicalDeviceProperties;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import dev.loki.lovisual.render.engine.rhi.backend.vulkan.util.VulkanRuntimeGuards;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.time.Instant;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* Opt-in Vulkan diagnostics. Compiled only when Gradle property lovisualVulkanDebug=true is used.
|
||||
*/
|
||||
public enum VulkanCrashDiagnostics {
|
||||
;
|
||||
|
||||
public static final boolean FORCE_DRIVER_DEBUG = bool("lovisual.vulkan.debug.forceDriverDebug", true);
|
||||
public static final boolean VERBOSE_DRAW_EVENTS = bool("lovisual.vulkan.debug.verboseDraw", false);
|
||||
public static final long MAX_FILE_BYTES = longProp("lovisual.vulkan.debug.maxBytes", 8L * 1024L * 1024L);
|
||||
public static final int MAX_EVENT_LINES = intProp("lovisual.vulkan.debug.maxEventLines", 256);
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger("LoVisual/VulkanDiag");
|
||||
private static final String DIAGNOSTIC_FILE_NAME = "lovisual-vulkan-diagnostics.log";
|
||||
private static final Object FILE_LOCK = new Object();
|
||||
private static final ConcurrentHashMap<String, AtomicInteger> EVENT_COUNTS = new ConcurrentHashMap<>();
|
||||
private static final AtomicLong WRITTEN_BYTES = new AtomicLong();
|
||||
private static volatile Path resolvedDiagnosticLog;
|
||||
private static volatile boolean headerWritten;
|
||||
private static volatile boolean truncated;
|
||||
|
||||
public static GpuDebugOptions forceDebugOptions(GpuDebugOptions options) {
|
||||
if (!FORCE_DRIVER_DEBUG) return options;
|
||||
int logLevel = options == null ? 4 : Math.max(options.logLevel(), 4);
|
||||
boolean labelsOriginally = options != null && options.useLabels();
|
||||
boolean validationOriginally = options != null && options.useValidationLayers();
|
||||
boolean labels = labelsOriginally && !VulkanRuntimeGuards.FORCE_DISABLE_VULKAN_DEBUG_LABELS;
|
||||
boolean validation = true;
|
||||
GpuDebugOptions forced = new GpuDebugOptions(logLevel, true, labels, validation);
|
||||
breadcrumb("debug-options.forced",
|
||||
"original", options,
|
||||
"forced", forced,
|
||||
"labelsOriginally", labelsOriginally,
|
||||
"labelsForcedOff", VulkanRuntimeGuards.FORCE_DISABLE_VULKAN_DEBUG_LABELS,
|
||||
"validationOriginally", validationOriginally,
|
||||
"validationEnabled", validation);
|
||||
LOGGER.warn("[LoVisual/VulkanDiag] forcing Vulkan debug utils + validation layers: {}", forced);
|
||||
return forced;
|
||||
}
|
||||
|
||||
public static void dumpDevice(VulkanInstance instance,
|
||||
VulkanPhysicalDevice physicalDevice,
|
||||
Set<String> requestedDeviceExtensions) {
|
||||
if (physicalDevice == null) {
|
||||
breadcrumb("device.dump.skipped", "reason", "physicalDevice=null");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
VkPhysicalDeviceProperties props = physicalDevice.vkPhysicalDeviceProperties();
|
||||
VkPhysicalDeviceLimits limits = props == null ? null : props.limits();
|
||||
String apiVersion = props == null ? "unknown" : version(props.apiVersion());
|
||||
String driverVersion = props == null ? "unknown" : version(props.driverVersion());
|
||||
String instanceExtensions = instance == null ? "<no-instance>" : join(instance.getEnabledExtensions());
|
||||
String deviceExtensions = join(requestedDeviceExtensions);
|
||||
String sampleCounts = limits == null ? "unknown" : String.format(
|
||||
"color=0x%X depth=0x%X stencil=0x%X noAttachments=0x%X",
|
||||
limits.framebufferColorSampleCounts(),
|
||||
limits.framebufferDepthSampleCounts(),
|
||||
limits.framebufferStencilSampleCounts(),
|
||||
limits.framebufferNoAttachmentsSampleCounts()
|
||||
);
|
||||
String queues = String.format("graphics=%s compute=%s transfer=%s",
|
||||
physicalDevice.graphicsQueueFamilyAndIndex(),
|
||||
physicalDevice.computeQueueFamilyAndIndex(),
|
||||
physicalDevice.transferQueueFamilyAndIndex());
|
||||
breadcrumb("device.dump",
|
||||
"device", physicalDevice.deviceName(),
|
||||
"vendor", physicalDevice.vendorName(),
|
||||
"type", physicalDevice.deviceType(),
|
||||
"driverInfo", physicalDevice.driverInfo(),
|
||||
"apiVersion", apiVersion,
|
||||
"driverVersionRaw", driverVersion,
|
||||
"vkVendorId", props == null ? "unknown" : props.vendorID(),
|
||||
"vkDeviceId", props == null ? "unknown" : props.deviceID(),
|
||||
"debugUtilsEnabled", instance != null && instance.debug() != null && instance.debug().enabled(),
|
||||
"instanceExtensions", instanceExtensions,
|
||||
"deviceExtensions", deviceExtensions,
|
||||
"sampleCounts", sampleCounts,
|
||||
"queues", queues,
|
||||
"stencil", "runtime-failsafe-only",
|
||||
"msaa", "config-and-capability-controlled",
|
||||
"hardDisableLabels", VulkanRuntimeGuards.FORCE_DISABLE_VULKAN_DEBUG_LABELS,
|
||||
"hardDisableCheckpoints", VulkanRuntimeGuards.FORCE_DISABLE_VULKAN_CHECKPOINTS,
|
||||
"maxBytes", MAX_FILE_BYTES,
|
||||
"verboseDraw", VERBOSE_DRAW_EVENTS,
|
||||
"maxEventLines", MAX_EVENT_LINES);
|
||||
} catch (Throwable t) {
|
||||
breadcrumb("device.dump.failed", "error", throwable(t));
|
||||
}
|
||||
}
|
||||
|
||||
public static void driverDebugMessage(int severity, int type, long callbackData) {
|
||||
if (callbackData == 0L) {
|
||||
breadcrumbQuiet("driver.debug.message", "severity", severityName(severity), "type", typeName(type), "message", "<null-callback-data>");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
VkDebugUtilsMessengerCallbackDataEXT data = VkDebugUtilsMessengerCallbackDataEXT.create(callbackData);
|
||||
breadcrumbQuiet("driver.debug.message",
|
||||
"severity", severityName(severity),
|
||||
"type", typeName(type),
|
||||
"id", data.pMessageIdNameString(),
|
||||
"number", data.messageIdNumber(),
|
||||
"message", sanitize(data.pMessageString()));
|
||||
} catch (Throwable t) {
|
||||
breadcrumbQuiet("driver.debug.message.failed",
|
||||
"severity", severity,
|
||||
"type", type,
|
||||
"error", throwable(t));
|
||||
}
|
||||
}
|
||||
|
||||
public static void breadcrumb(String event, Object... keyValues) {
|
||||
String line = formatLine(event, keyValues);
|
||||
LOGGER.warn(line);
|
||||
writeLine(event, line);
|
||||
}
|
||||
|
||||
public static void breadcrumbQuiet(String event, Object... keyValues) {
|
||||
if (isNoisyRenderEvent(event) && !VERBOSE_DRAW_EVENTS) return;
|
||||
writeLine(event, formatLine(event, keyValues));
|
||||
}
|
||||
|
||||
public static void breadcrumbDraw(String event, Object... keyValues) {
|
||||
if (!VERBOSE_DRAW_EVENTS) return;
|
||||
writeLine(event, formatLine(event, keyValues));
|
||||
}
|
||||
|
||||
public static String throwable(Throwable t) {
|
||||
if (t == null) return "null";
|
||||
return t.getClass().getName() + ": " + String.valueOf(t.getMessage());
|
||||
}
|
||||
|
||||
private static boolean isNoisyRenderEvent(String event) {
|
||||
if (event == null) return false;
|
||||
return event.startsWith("renderpass.draw")
|
||||
|| event.startsWith("renderpass.multidraw")
|
||||
|| event.startsWith("renderpass.bindtexture")
|
||||
|| event.startsWith("renderpass.uniform")
|
||||
|| event.startsWith("renderpass.vertexbuffer")
|
||||
|| event.startsWith("renderpass.indexbuffer")
|
||||
|| event.startsWith("renderpass.scissor")
|
||||
|| event.startsWith("renderpass.pushdescriptors");
|
||||
}
|
||||
|
||||
private static boolean allowEvent(String event) {
|
||||
if (event == null || MAX_EVENT_LINES <= 0) return true;
|
||||
AtomicInteger counter = EVENT_COUNTS.computeIfAbsent(event, ignored -> new AtomicInteger());
|
||||
int count = counter.incrementAndGet();
|
||||
if (count <= MAX_EVENT_LINES) return true;
|
||||
if (count == MAX_EVENT_LINES + 1) {
|
||||
writeLineDirect(formatLine("event.suppressed", "event", event, "limit", MAX_EVENT_LINES));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String formatLine(String event, Object... keyValues) {
|
||||
StringBuilder sb = new StringBuilder(256);
|
||||
sb.append('[').append(Instant.now()).append("] ").append(event == null ? "<null-event>" : event);
|
||||
if (keyValues != null) {
|
||||
for (int i = 0; i < keyValues.length; i += 2) {
|
||||
Object key = keyValues[i];
|
||||
Object value = i + 1 < keyValues.length ? keyValues[i + 1] : "<missing>";
|
||||
sb.append(' ').append(String.valueOf(key)).append('=').append(sanitize(String.valueOf(value)));
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String sanitize(String value) {
|
||||
if (value == null) return "null";
|
||||
return value.replace('\r', ' ').replace('\n', ' ').replace('\t', ' ');
|
||||
}
|
||||
|
||||
private static String join(Collection<String> values) {
|
||||
if (values == null || values.isEmpty()) return "<empty>";
|
||||
StringJoiner joiner = new StringJoiner(",");
|
||||
for (String value : values) joiner.add(value);
|
||||
return joiner.toString();
|
||||
}
|
||||
|
||||
private static String version(int version) {
|
||||
return VK12.VK_VERSION_MAJOR(version) + "." + VK12.VK_VERSION_MINOR(version) + "." + VK12.VK_VERSION_PATCH(version)
|
||||
+ " (0x" + Integer.toHexString(version) + ")";
|
||||
}
|
||||
|
||||
private static String severityName(int severity) {
|
||||
if ((severity & 0x1000) != 0) return "ERROR(0x" + Integer.toHexString(severity) + ")";
|
||||
if ((severity & 0x0100) != 0) return "WARNING(0x" + Integer.toHexString(severity) + ")";
|
||||
if ((severity & 0x0010) != 0) return "INFO(0x" + Integer.toHexString(severity) + ")";
|
||||
if ((severity & 0x0001) != 0) return "VERBOSE(0x" + Integer.toHexString(severity) + ")";
|
||||
return "UNKNOWN(0x" + Integer.toHexString(severity) + ")";
|
||||
}
|
||||
|
||||
private static String typeName(int type) {
|
||||
StringJoiner joiner = new StringJoiner("|");
|
||||
if ((type & 0x1) != 0) joiner.add("GENERAL");
|
||||
if ((type & 0x2) != 0) joiner.add("VALIDATION");
|
||||
if ((type & 0x4) != 0) joiner.add("PERFORMANCE");
|
||||
String joined = joiner.toString();
|
||||
return joined.isEmpty() ? "UNKNOWN(0x" + Integer.toHexString(type) + ")" : joined + "(0x" + Integer.toHexString(type) + ")";
|
||||
}
|
||||
|
||||
private static Path diagnosticLogPath() {
|
||||
Path cached = resolvedDiagnosticLog;
|
||||
if (cached != null) return cached;
|
||||
Path resolved = resolveMinecraftLogsPath();
|
||||
resolvedDiagnosticLog = resolved;
|
||||
return resolved;
|
||||
}
|
||||
|
||||
private static Path resolveMinecraftLogsPath() {
|
||||
try {
|
||||
Minecraft minecraft = Minecraft.getInstance();
|
||||
if (minecraft != null && minecraft.gameDirectory != null) {
|
||||
return minecraft.gameDirectory.toPath().resolve("logs").resolve(DIAGNOSTIC_FILE_NAME);
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
// Vulkan init can run early enough that Minecraft may not be fully accessible.
|
||||
}
|
||||
return Path.of("logs", DIAGNOSTIC_FILE_NAME);
|
||||
}
|
||||
|
||||
private static void writeLine(String event, String line) {
|
||||
if (!allowEvent(event)) return;
|
||||
writeLineDirect(line);
|
||||
}
|
||||
|
||||
private static void writeLineDirect(String line) {
|
||||
synchronized (FILE_LOCK) {
|
||||
try {
|
||||
Path log = diagnosticLogPath();
|
||||
Path parent = log.getParent();
|
||||
if (parent != null) Files.createDirectories(parent);
|
||||
if (!headerWritten) {
|
||||
String header = "=== LoVisual Vulkan diagnostics ===\n" +
|
||||
"modId=simplefullbright\n" +
|
||||
"entrypoint=dev.loki.lovisual.LoVisual\n" +
|
||||
"path=" + log.toAbsolutePath() + '\n' +
|
||||
"stencil=runtime-failsafe-only" + '\n' +
|
||||
"msaa=config-and-capability-controlled" + '\n' +
|
||||
"hardDisableLabels=" + VulkanRuntimeGuards.FORCE_DISABLE_VULKAN_DEBUG_LABELS + '\n' +
|
||||
"hardDisableCheckpoints=" + VulkanRuntimeGuards.FORCE_DISABLE_VULKAN_CHECKPOINTS + '\n' +
|
||||
"maxBytes=" + MAX_FILE_BYTES + '\n' +
|
||||
"maxEventLines=" + MAX_EVENT_LINES + '\n' +
|
||||
"verboseDraw=" + VERBOSE_DRAW_EVENTS + '\n';
|
||||
Files.writeString(log, header,
|
||||
StandardOpenOption.CREATE,
|
||||
StandardOpenOption.TRUNCATE_EXISTING,
|
||||
StandardOpenOption.WRITE);
|
||||
WRITTEN_BYTES.set(header.getBytes(StandardCharsets.UTF_8).length);
|
||||
headerWritten = true;
|
||||
}
|
||||
if (MAX_FILE_BYTES > 0 && WRITTEN_BYTES.get() >= MAX_FILE_BYTES) {
|
||||
if (!truncated) {
|
||||
String marker = formatLine("file.truncated", "maxBytes", MAX_FILE_BYTES) + System.lineSeparator();
|
||||
Files.writeString(log, marker, StandardOpenOption.CREATE, StandardOpenOption.APPEND, StandardOpenOption.WRITE);
|
||||
WRITTEN_BYTES.addAndGet(marker.getBytes(StandardCharsets.UTF_8).length);
|
||||
truncated = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
String out = line + System.lineSeparator();
|
||||
Files.writeString(log, out,
|
||||
StandardOpenOption.CREATE,
|
||||
StandardOpenOption.APPEND,
|
||||
StandardOpenOption.WRITE);
|
||||
WRITTEN_BYTES.addAndGet(out.getBytes(StandardCharsets.UTF_8).length);
|
||||
} catch (IOException ignored) {
|
||||
// The normal Minecraft log still receives LOGGER output for explicit breadcrumb(...).
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean bool(String key, boolean fallback) {
|
||||
String value = System.getProperty(key);
|
||||
if (value == null || value.isBlank()) return fallback;
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
private static int intProp(String key, int fallback) {
|
||||
String value = System.getProperty(key);
|
||||
if (value == null || value.isBlank()) return fallback;
|
||||
try {
|
||||
return Integer.parseInt(value.trim());
|
||||
} catch (NumberFormatException ignored) {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
private static long longProp(String key, long fallback) {
|
||||
String value = System.getProperty(key);
|
||||
if (value == null || value.isBlank()) return fallback;
|
||||
try {
|
||||
return Long.parseLong(value.trim());
|
||||
} catch (NumberFormatException ignored) {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"required": true,
|
||||
"plugin": "dev.loki.lovisual.mixins.LoVisualMixinPlugin",
|
||||
"minVersion": "0.8",
|
||||
"package": "dev.loki.lovisual.mixins",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"client": [
|
||||
"CommandEncoderDiagnosticsMixin",
|
||||
"RenderPassWrapperDiagnosticsMixin",
|
||||
"VulkanBackendMixin",
|
||||
"VulkanCheckpointDiagnosticsMixin",
|
||||
"VulkanCommandEncoderDiagnosticsMixin",
|
||||
"VulkanDebugMessageMixin",
|
||||
"VulkanDeviceDiagnosticsMixin",
|
||||
"VulkanQueueSubmissionMixin",
|
||||
"VulkanRenderPassDiagnosticsMixin",
|
||||
"VulkanRenderPipelineDiagnosticsMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue