Shader graph: Clamp is the Output's default edge, and the sheet's own mode is a constant of its own (#81)
game-maker / suite (push) Successful in 9m16s

The default of the Edge param and the address mode the shared sheet physically
carries were the same constant; changing the value would have turned every
sentinel with it and given each shader sprite a full copy of its texture.
SHEET_EDGE ('mirror', what TextureLibrary uploads) is now separate from
DEFAULT_EDGE ('clamp'). A graph whose Sample reads nothing but its own pixel
draws with the plain sheet whatever the Edge says, so the common shader costs
no second copy.

Without a shader the quad's coordinates never leave 0..1, and mirror-repeat
reads like clamp within a texel - so clamp is what a sprite shows anyway.
This commit is contained in:
admin committed 2026-09-13 21:33:41 +02:00
1 parent 03c4323794
commit e0c459ce92
7 files changed
+74 -20

No files matched your search

+23 -7
View File
@@ -36,6 +36,8 @@ import {
import { inlineInputs } from '../../src/core/graph/NodeRegistry';
import { compileShader, shaderFingerprint } from '../../src/core/shader/compile';
import {
DEFAULT_EDGE,
EDGE_PARAM,
SHADER_NODES,
SHADER_NUMBER,
SHADER_OUTPUT,
@@ -47,6 +49,7 @@ import {
SHADER_UV,
ShaderOutput,
ShaderSample,
SHEET_EDGE,
} from '../../src/core/shader/nodes';
import { deserializeProject } from '../../src/core/store/serialize';
import { paletteEntries } from '../../src/editor/graph/paletteEntries';
@@ -414,16 +417,29 @@ describe('compileShader', () => {
const s = shaderEditor(f, 'Idle');
s.add(SHADER_TIME);
const out = s.add(SHADER_OUTPUT);
let compiled = compileShader(s.asset().graph).compiled!;
expect(compiled).toMatchObject({ time: false, edge: 'mirror' });
expect(compiled.fragment).not.toContain('uTime');
const params = s.asset().graph.nodes.find((n) => n.id === out)!.params;
params.edge = 'clamp';
expect(compileShader(s.asset().graph).compiled!.edge).toBe('clamp');
// A new Output carries the default, so a graph never silently changes when the default does.
expect(params[EDGE_PARAM]).toBe('clamp');
let compiled = compileShader(s.asset().graph).compiled!;
expect(compiled).toMatchObject({ time: false, edge: SHEET_EDGE });
expect(compiled.fragment).not.toContain('uTime');
// Nothing reads past the border here, so no mode can show – and none costs a second picture.
params[EDGE_PARAM] = 'repeat';
expect(compileShader(s.asset().graph).compiled!.edge).toBe(SHEET_EDGE);
const uv = s.add(SHADER_UV);
const shift = s.add('ShaderAdd', { b: 0.5 });
const sample = s.add(SHADER_SAMPLE);
s.link(uv, 'u', shift, 'a');
s.link(shift, 'result', sample, 'u');
s.link(sample, 'color', out, 'color');
expect(compileShader(s.asset().graph).compiled!.edge).toBe('repeat');
// A value the dropdown never offers reads as the default rather than as a mode.
params.edge = 'sideways';
params[EDGE_PARAM] = 'sideways';
compiled = compileShader(s.asset().graph).compiled!;
expect(compiled.edge).toBe('mirror');
expect(compiled.edge).toBe(DEFAULT_EDGE);
// A graph from before the setting existed has no value at all and takes the default too (#81).
delete params[EDGE_PARAM];
expect(compileShader(s.asset().graph).compiled!.edge).toBe('clamp');
});
it('writes Fract, Step and Floor as the GLSL calls, per channel for a colour', () => {