Compare commits

...
Sign in to create a new pull request.

4 commits

Author SHA1 Message Date
Vitaly Turovsky
c139c9365d finish 2025-06-05 18:33:44 +03:00
Vitaly Turovsky
eff87f42e5 finish! 2025-06-05 17:04:18 +03:00
Vitaly Turovsky
c151e414d9 Merge branch 'next' into renderer-debug 2025-06-05 16:48:46 +03:00
Vitaly Turovsky
96a6b790db renderer debug 2025-05-06 16:15:42 +03:00
11 changed files with 225 additions and 1 deletions

View file

@ -8,6 +8,7 @@ import { ItemsRenderer } from 'mc-assets/dist/itemsRenderer'
import { WorldBlockProvider } from 'mc-assets/dist/worldBlockProvider' import { WorldBlockProvider } from 'mc-assets/dist/worldBlockProvider'
import { generateSpiralMatrix } from 'flying-squid/dist/utils' import { generateSpiralMatrix } from 'flying-squid/dist/utils'
import { subscribeKey } from 'valtio/utils' import { subscribeKey } from 'valtio/utils'
import { proxy } from 'valtio'
import { dynamicMcDataFiles } from '../../buildMesherConfig.mjs' import { dynamicMcDataFiles } from '../../buildMesherConfig.mjs'
import { toMajorVersion } from '../../../src/utils' import { toMajorVersion } from '../../../src/utils'
import { ResourcesManager } from '../../../src/resourcesManager' import { ResourcesManager } from '../../../src/resourcesManager'
@ -49,6 +50,7 @@ export const defaultWorldRendererConfig = {
fetchPlayerSkins: true, fetchPlayerSkins: true,
highlightBlockColor: 'blue', highlightBlockColor: 'blue',
foreground: true, foreground: true,
enableDebugOverlay: false,
_experimentalSmoothChunkLoading: true, _experimentalSmoothChunkLoading: true,
_renderByChunks: false _renderByChunks: false
} }
@ -60,6 +62,17 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
worldReadyPromise = this.worldReadyResolvers.promise worldReadyPromise = this.worldReadyResolvers.promise
timeOfTheDay = 0 timeOfTheDay = 0
worldSizeParams = { minY: 0, worldHeight: 256 } worldSizeParams = { minY: 0, worldHeight: 256 }
reactiveDebugParams = proxy({
stopRendering: false,
chunksRenderAboveOverride: undefined as number | undefined,
chunksRenderAboveEnabled: false,
chunksRenderBelowOverride: undefined as number | undefined,
chunksRenderBelowEnabled: false,
chunksRenderDistanceOverride: undefined as number | undefined,
chunksRenderDistanceEnabled: false,
disableEntities: false,
// disableParticles: false
})
active = false active = false
@ -315,6 +328,11 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
subscribeKey(this.worldRendererConfig, key, callback) subscribeKey(this.worldRendererConfig, key, callback)
} }
onReactiveDebugUpdated<T extends keyof typeof this.reactiveDebugParams>(key: T, callback: (value: typeof this.reactiveDebugParams[T]) => void) {
callback(this.reactiveDebugParams[key])
subscribeKey(this.reactiveDebugParams, key, callback)
}
watchReactivePlayerState () { watchReactivePlayerState () {
this.onReactiveValueUpdated('backgroundColor', (value) => { this.onReactiveValueUpdated('backgroundColor', (value) => {
this.changeBackgroundColor(value) this.changeBackgroundColor(value)

View file

@ -455,7 +455,39 @@ export class WorldRendererThree extends WorldRendererCommon {
this.cameraShake.setBaseRotation(pitch, yaw) this.cameraShake.setBaseRotation(pitch, yaw)
} }
debugChunksVisibilityOverride () {
const { chunksRenderAboveOverride, chunksRenderBelowOverride, chunksRenderDistanceOverride, chunksRenderAboveEnabled, chunksRenderBelowEnabled, chunksRenderDistanceEnabled } = this.reactiveDebugParams
const baseY = this.cameraSectionPos.y * 16
if (
this.displayOptions.inWorldRenderingConfig.enableDebugOverlay &&
chunksRenderAboveOverride !== undefined ||
chunksRenderBelowOverride !== undefined ||
chunksRenderDistanceOverride !== undefined
) {
for (const [key, object] of Object.entries(this.sectionObjects)) {
const [x, y, z] = key.split(',').map(Number)
const isVisible =
// eslint-disable-next-line no-constant-binary-expression, sonarjs/no-redundant-boolean
(chunksRenderAboveEnabled && chunksRenderAboveOverride !== undefined) ? y <= (baseY + chunksRenderAboveOverride) : true &&
// eslint-disable-next-line @stylistic/indent-binary-ops, no-constant-binary-expression, sonarjs/no-redundant-boolean
(chunksRenderBelowEnabled && chunksRenderBelowOverride !== undefined) ? y >= (baseY - chunksRenderBelowOverride) : true &&
// eslint-disable-next-line @stylistic/indent-binary-ops
(chunksRenderDistanceEnabled && chunksRenderDistanceOverride !== undefined) ? Math.abs(y - baseY) <= chunksRenderDistanceOverride : true
object.visible = isVisible
}
} else {
for (const object of Object.values(this.sectionObjects)) {
object.visible = true
}
}
}
render (sizeChanged = false) { render (sizeChanged = false) {
if (this.reactiveDebugParams.stopRendering) return
this.debugChunksVisibilityOverride()
const start = performance.now() const start = performance.now()
this.lastRendered = performance.now() this.lastRendered = performance.now()
this.cursorBlock.render() this.cursorBlock.render()
@ -468,7 +500,9 @@ export class WorldRendererThree extends WorldRendererCommon {
this.camera.updateProjectionMatrix() this.camera.updateProjectionMatrix()
} }
this.entities.render() if (!this.reactiveDebugParams.disableEntities) {
this.entities.render()
}
// eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style
const cam = this.cameraGroupVr instanceof THREE.Group ? this.cameraGroupVr.children.find(child => child instanceof THREE.PerspectiveCamera) as THREE.PerspectiveCamera : this.camera const cam = this.cameraGroupVr instanceof THREE.Group ? this.cameraGroupVr.children.find(child => child instanceof THREE.PerspectiveCamera) as THREE.PerspectiveCamera : this.camera

View file

@ -678,6 +678,12 @@ export const f3Keybinds: Array<{
}, },
mobileTitle: 'Show Chunks Debug', mobileTitle: 'Show Chunks Debug',
}, },
{
action () {
showModal({ reactType: 'renderer-debug' })
},
mobileTitle: 'Renderer Debug Menu',
},
{ {
key: 'KeyY', key: 'KeyY',
async action () { async action () {

View file

@ -109,6 +109,9 @@ export const guiOptionsScheme: {
'none' 'none'
], ],
}, },
rendererPerfDebugOverlay: {
text: 'Performance Debug',
}
}, },
{ {
custom () { custom () {

View file

@ -106,6 +106,7 @@ const defaultOptions = {
vrSupport: true, // doesn't directly affect the VR mode, should only disable the button which is annoying to android users vrSupport: true, // doesn't directly affect the VR mode, should only disable the button which is annoying to android users
vrPageGameRendering: false, vrPageGameRendering: false,
renderDebug: 'basic' as 'none' | 'advanced' | 'basic', renderDebug: 'basic' as 'none' | 'advanced' | 'basic',
rendererPerfDebugOverlay: false,
// advanced bot options // advanced bot options
autoRespawn: false, autoRespawn: false,

View file

@ -0,0 +1,115 @@
import { WorldRendererCommon } from 'renderer/viewer/lib/worldrendererCommon'
import { useState } from 'react'
import { useSnapshot } from 'valtio'
import { options } from '../optionsStorage'
import Screen from './Screen'
import Button from './Button'
import Slider from './Slider'
import styles from './rendererDebugMenu.module.css'
export default () => {
const worldRenderer = window.world as WorldRendererCommon
const { reactiveDebugParams } = worldRenderer
const { chunksRenderAboveEnabled, chunksRenderBelowEnabled, chunksRenderDistanceEnabled, chunksRenderAboveOverride, chunksRenderBelowOverride, chunksRenderDistanceOverride, stopRendering, disableEntities } = useSnapshot(reactiveDebugParams)
const { rendererPerfDebugOverlay } = useSnapshot(options)
// Helper to round values to nearest step
const roundToStep = (value: number, step: number) => Math.round(value / step) * step
if (!rendererPerfDebugOverlay) return null
return <div className={styles.container}>
<div className={styles.column}>
<h3>Rendering Controls</h3>
<Button
label={stopRendering ? 'Start Rendering' : 'Stop Rendering'}
onClick={() => { reactiveDebugParams.stopRendering = !reactiveDebugParams.stopRendering }}
overlayColor={stopRendering ? 'red' : undefined}
/>
<Button
label={disableEntities ? 'Enable Entities' : 'Disable Entities'}
onClick={() => { reactiveDebugParams.disableEntities = !reactiveDebugParams.disableEntities }}
overlayColor={disableEntities ? 'red' : undefined}
/>
</div>
<div className={styles.column}>
<h3>Chunks Render Settings</h3>
<div className={styles.sliderGroup}>
<Button
label={chunksRenderAboveEnabled ? 'Disable Above Override' : 'Enable Above Override'}
onClick={() => {
const newState = !chunksRenderAboveEnabled
reactiveDebugParams.chunksRenderAboveEnabled = newState
if (newState) { reactiveDebugParams.chunksRenderAboveOverride = 0 } else { reactiveDebugParams.chunksRenderAboveOverride = undefined }
}}
/>
<Slider
label="Chunks Above"
min={0}
max={256}
value={chunksRenderAboveOverride ?? 0}
style={{ width: '100%', }}
updateValue={(value) => {
const roundedValue = roundToStep(value, 16)
reactiveDebugParams.chunksRenderAboveOverride = roundedValue
}}
disabledReason={chunksRenderAboveEnabled ? undefined : 'Override not enabled'}
unit=""
valueDisplay={roundToStep(reactiveDebugParams.chunksRenderAboveOverride ?? 0, 16)}
/>
</div>
<div className={styles.sliderGroup}>
<Button
label={chunksRenderBelowEnabled ? 'Disable Below Override' : 'Enable Below Override'}
onClick={() => {
const newState = !chunksRenderBelowEnabled
reactiveDebugParams.chunksRenderBelowEnabled = newState
if (newState) { reactiveDebugParams.chunksRenderBelowOverride = 0 } else { reactiveDebugParams.chunksRenderBelowOverride = undefined }
}}
/>
<Slider
label="Chunks Below"
min={0}
max={256}
style={{ width: '100%', }}
value={chunksRenderBelowOverride ?? 0}
updateValue={(value) => {
const roundedValue = roundToStep(value, 16)
reactiveDebugParams.chunksRenderBelowOverride = roundedValue
}}
disabledReason={chunksRenderBelowEnabled ? undefined : 'Override not enabled'}
unit=""
valueDisplay={roundToStep(reactiveDebugParams.chunksRenderBelowOverride ?? 0, 16)}
/>
</div>
{/* <div className={styles.sliderGroup}>
<Button
label={chunksRenderDistanceEnabled ? 'Disable Distance Override' : 'Enable Distance Override'}
onClick={() => {
const newState = !chunksRenderDistanceEnabled
reactiveDebugParams.chunksRenderDistanceEnabled = newState
if (newState) { reactiveDebugParams.chunksRenderDistanceOverride = 8 } else { reactiveDebugParams.chunksRenderDistanceOverride = undefined }
}}
/>
<Slider
label="Render Distance"
min={1}
max={32}
style={{ width: '100%', }}
value={chunksRenderDistanceOverride ?? 8}
updateValue={(value) => {
const roundedValue = Math.round(value)
reactiveDebugParams.chunksRenderDistanceOverride = roundedValue
}}
disabledReason={chunksRenderDistanceEnabled ? undefined : 'Override not enabled'}
unit=""
valueDisplay={Math.round(reactiveDebugParams.chunksRenderDistanceOverride ?? 8)}
/>
</div> */}
</div>
</div>
}

View file

@ -4,6 +4,7 @@
position: relative; position: relative;
width: 200px; width: 200px;
min-height: calc(20px * var(--scale)); min-height: calc(20px * var(--scale));
max-height: calc(20px * var(--scale));
font-family: minecraft, mojangles, monospace; font-family: minecraft, mojangles, monospace;
font-size: 10px; font-size: 10px;
color: white; color: white;

View file

@ -0,0 +1,34 @@
.container {
display: flex;
flex-direction: row;
justify-content: space-between;
gap: 20px;
padding: 10px;
height: 100%;
padding-top: env(safe-area-inset-top, 10px);
padding-left: env(safe-area-inset-left, 10px);
padding-right: env(safe-area-inset-right, 10px);
padding-bottom: env(safe-area-inset-bottom, 10px);
}
.column {
display: flex;
flex-direction: column;
gap: 10px;
min-width: 200px;
}
.column h3 {
margin: 0;
padding: 0;
font-size: 16px;
color: white;
text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.8);
}
.sliderGroup {
display: flex;
flex-direction: column;
gap: 5px;
margin-bottom: 10px;
}

View file

@ -0,0 +1,9 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
column: string;
container: string;
sliderGroup: string;
}
declare const cssExports: CssExports;
export default cssExports;

View file

@ -61,6 +61,7 @@ import ControDebug from './react/ControDebug'
import ChunksDebug from './react/ChunksDebug' import ChunksDebug from './react/ChunksDebug'
import ChunksDebugScreen from './react/ChunksDebugScreen' import ChunksDebugScreen from './react/ChunksDebugScreen'
import DebugResponseTimeIndicator from './react/debugs/DebugResponseTimeIndicator' import DebugResponseTimeIndicator from './react/debugs/DebugResponseTimeIndicator'
import RendererDebugMenu from './react/RendererDebugMenu'
import CreditsAboutModal from './react/CreditsAboutModal' import CreditsAboutModal from './react/CreditsAboutModal'
import GlobalOverlayHints from './react/GlobalOverlayHints' import GlobalOverlayHints from './react/GlobalOverlayHints'
@ -167,6 +168,7 @@ const InGameUi = () => {
{!disabledUiParts.includes('bossbars') && displayBossBars && <BossBarOverlayProvider />} {!disabledUiParts.includes('bossbars') && displayBossBars && <BossBarOverlayProvider />}
<VoiceMicrophone /> <VoiceMicrophone />
<ChunksDebugScreen /> <ChunksDebugScreen />
<RendererDebugMenu />
</PerComponentErrorBoundary> </PerComponentErrorBoundary>
</div> </div>

View file

@ -83,6 +83,7 @@ export const watchOptionsAfterViewerInit = () => {
watchValue(options, o => { watchValue(options, o => {
appViewer.inWorldRenderingConfig.vrSupport = o.vrSupport appViewer.inWorldRenderingConfig.vrSupport = o.vrSupport
appViewer.inWorldRenderingConfig.vrPageGameRendering = o.vrPageGameRendering appViewer.inWorldRenderingConfig.vrPageGameRendering = o.vrPageGameRendering
appViewer.inWorldRenderingConfig.enableDebugOverlay = o.rendererPerfDebugOverlay
}) })
watchValue(options, (o, isChanged) => { watchValue(options, (o, isChanged) => {