Ask the driver what filter a texture actually got
Chasing the flaky pixel-art check needs to tell two things apart that look identical in a screenshot: a scale mode the model never changed, and one that changed but never reached the sampler. Only the driver can answer the second, so `__gm.textureFilter(file)` returns the model's scaleMode next to `gl.getTexParameter` on the uploaded texture. It binds through `renderer.texture.bindSource` rather than a raw `bindTexture`, so measuring does not corrupt Pixi's idea of what is bound and become a source of flakiness itself. `TextureLibrary.peek` looks a picture up without starting a load or touching its scale mode, which `get` would do. Sixty measurements later the filter path is cleared: model and driver agreed every time, across repeated switches, after the full module run-up, and through a forced context loss. The instrument stays; the note in todo-notes/flaky-pixel-art-filter.md stays open.
This commit is contained in:
1 parent
e9634f0f0d
commit
2f4dc843d7
2 files changed
+36
No files matched your search
@@ -2,6 +2,7 @@ import { type Application, Color, type ColorSource, type Container, Rectangle, T
|
||||
import { resolveObjects } from '../core/model/instance';
|
||||
import { findObjectHolder } from '../core/model/Project';
|
||||
import { Button, ui, Widget } from '../ui';
|
||||
import { textureLibrary } from '../viewport/TextureLibrary';
|
||||
import type { CentreTab, Editor } from './Editor';
|
||||
import type { EditorState } from './state';
|
||||
|
||||
@@ -200,6 +201,36 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
|
||||
const hex = (value: number | undefined) => (value ?? 0).toString(16).padStart(2, '0');
|
||||
return `#${hex(pixels[0])}${hex(pixels[1])}${hex(pixels[2])}`;
|
||||
},
|
||||
/**
|
||||
* What the GPU was actually told about a picture's filter, next to what the model asked for.
|
||||
* The two can disagree – that is the difference between a wrong value and a value that never
|
||||
* reached the sampler – and only the driver can be asked for the second one.
|
||||
*/
|
||||
textureFilter: (file: string) => {
|
||||
const texture = textureLibrary.peek(file);
|
||||
if (!texture) return null;
|
||||
const source = texture.source;
|
||||
const renderer = app.renderer as unknown as {
|
||||
gl?: WebGL2RenderingContext;
|
||||
uid: number;
|
||||
texture: { bindSource(source: unknown, location: number): void };
|
||||
};
|
||||
const gl = renderer.gl;
|
||||
const uploaded = Boolean(
|
||||
(source as unknown as { _gpuData: Record<number, unknown> })._gpuData[renderer.uid],
|
||||
);
|
||||
let magFilter = 0;
|
||||
if (gl) {
|
||||
// Through Pixi rather than a raw `bindTexture`, so its idea of what is bound stays true.
|
||||
renderer.texture.bindSource(source, 0);
|
||||
magFilter = gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER) as number;
|
||||
}
|
||||
return {
|
||||
scaleMode: source.scaleMode,
|
||||
gpu: magFilter === gl?.NEAREST ? 'nearest' : magFilter === gl?.LINEAR ? 'linear' : 'none',
|
||||
uploaded,
|
||||
};
|
||||
},
|
||||
fps: () => editor.fps,
|
||||
tab: () => editor.tab,
|
||||
showTab: (tab: CentreTab) => editor.showTab(tab),
|
||||
|
||||
@@ -42,6 +42,11 @@ export class TextureLibrary {
|
||||
return texture;
|
||||
}
|
||||
|
||||
/** The picture if it is in memory, without starting a load or touching the scale mode. */
|
||||
peek(file: string): Texture | null {
|
||||
return this.loaded.get(file) ?? null;
|
||||
}
|
||||
|
||||
/** Runs `fn` whenever a picture has arrived; returns the unsubscribe. */
|
||||
onChange(fn: () => void): () => void {
|
||||
this.listeners.add(fn);
|
||||
|
||||
Reference in new issue
Block a user