Skip to content

Commit 8ee607d

Browse files
chore(tests): add glslang for validation
1 parent d5c4982 commit 8ee607d

File tree

6 files changed

+1395
-1
lines changed

6 files changed

+1395
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
.DS_Store
22
node_modules
33
dist
4+
tests/shaders/bin
45

56
# Editor directories and files
67
.idea

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@
2626
"exports": "./dist/index.js",
2727
"sideEffects": false,
2828
"devDependencies": {
29+
"@types/decompress": "^4.2.7",
30+
"@types/download": "^8.0.5",
2931
"@types/node": "^24.6.2",
32+
"decompress": "^4.2.1",
33+
"download": "^8.0.0",
34+
"rimraf": "^6.0.1",
35+
"temp-dir": "^3.0.0",
3036
"typescript": "^5.9.3",
3137
"vite": "^7.1.8",
3238
"vitest": "^3.2.4"

tests/index.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { describe, it, expect } from 'vitest'
22
import { tokenize, minify } from 'shaderkit'
3+
import { glslang } from './shaders/glslang'
4+
import * as fs from 'node:fs'
5+
import * as path from 'node:path'
6+
import { rimrafSync } from 'rimraf'
37

48
const glsl = /* glsl */ `#version 300 es
59
precision mediump float;
@@ -190,4 +194,21 @@ describe('minify', () => {
190194
expect(minify(shader, { mangle: true, mangleExternals: true, mangleMap })).toMatchSnapshot()
191195
expect(mangleMap).toMatchSnapshot()
192196
})
197+
198+
it('validates mangling works correctly with glslang', () => {
199+
glslang('tests/shaders/test.frag')
200+
201+
const shader = fs.readFileSync(path.resolve(process.cwd(), 'tests/shaders/test.frag'), { encoding: 'utf-8' })
202+
const minified = minify(shader, { mangle: true, mangleExternals: true })
203+
fs.writeFileSync(path.resolve(process.cwd(), 'tests/shaders/test.min.frag'), minified, { encoding: 'utf-8' })
204+
205+
try {
206+
glslang('tests/shaders/test.min.frag')
207+
} catch (error: any) {
208+
error.message = '\n' + minified + '\n\n' + error.message
209+
throw error
210+
} finally {
211+
rimrafSync(path.resolve(process.cwd(), 'tests/shaders/test.min.frag'))
212+
}
213+
})
193214
})

tests/shaders/glslang.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import * as fs from 'node:fs'
2+
import * as os from 'node:os'
3+
import * as path from 'node:path'
4+
import * as url from 'node:url'
5+
import { execFileSync } from 'node:child_process'
6+
import download from 'download'
7+
import tempDirRoot from 'temp-dir'
8+
import extract from 'decompress'
9+
import { rimrafSync } from 'rimraf'
10+
11+
const platform = os.platform()
12+
const arch = os.arch()
13+
if (platform !== 'linux' && platform !== 'darwin' && platform !== 'win32') {
14+
throw new Error(`Unsupported platform: ${platform}`)
15+
}
16+
if (!(arch === 'x64' || (arch === 'arm64' && platform === 'darwin'))) {
17+
throw new Error(`Unsupported architecture: ${arch}`)
18+
}
19+
20+
const filenames = {
21+
darwin: 'glslang-master-osx-Release',
22+
linux: 'glslang-master-linux-Release',
23+
win32: 'glslang-master-windows-x64-Release',
24+
}
25+
26+
const dirname = path.dirname(url.fileURLToPath(import.meta.url))
27+
const filename = filenames[platform]
28+
const suffix = platform === 'win32' ? '.exe' : ''
29+
const archiveURL = `https://github.com/KhronosGroup/glslang/releases/download/master-tot/${filename}.zip`
30+
const tempDir = path.resolve(tempDirRoot, 'glslang-validator-prebuilt')
31+
const zipPath = path.resolve(tempDir, `${filename}.zip`)
32+
const unzippedBinPath = path.resolve(tempDir, `bin/glslangValidator${suffix}`)
33+
const dstBinPath = path.resolve(dirname, `bin/glslangValidator${suffix}`)
34+
const dstBinDir = path.resolve(dirname, `bin`)
35+
36+
if (!fs.existsSync(dstBinPath)) {
37+
rimrafSync(tempDir)
38+
fs.mkdirSync(tempDir, { recursive: true })
39+
await download(archiveURL, tempDir)
40+
await extract(zipPath, tempDir)
41+
rimrafSync(dstBinDir)
42+
fs.mkdirSync(dstBinDir, { recursive: true })
43+
fs.copyFileSync(unzippedBinPath, dstBinPath)
44+
fs.chmodSync(dstBinPath, '755')
45+
}
46+
47+
export function glslang(...args: string[]): void {
48+
try {
49+
execFileSync(dstBinPath, args.length === 0 ? ['--version'] : args, { encoding: 'utf-8' })
50+
} catch (error: any) {
51+
throw new Error(error.stdout)
52+
}
53+
}

tests/shaders/test.frag

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#version 300 es
2+
precision mediump float;
3+
4+
// single line
5+
6+
/*
7+
multiline
8+
*/
9+
10+
#define PLUS (n) \
11+
n += 1
12+
13+
#define TEST // inline comment
14+
#if defined( TEST )
15+
const bool isTest = true;
16+
#endif
17+
18+
uniform float foo, bar;
19+
20+
uniform sampler2D map;
21+
in vec2 vUv;
22+
out vec4 pc_FragColor;
23+
24+
// #include <three_test>
25+
26+
layout(std140) uniform Uniforms1 {
27+
mat4 projectionMatrix;
28+
mat4 modelViewMatrix;
29+
mat3 normalMatrix;
30+
float one, two;
31+
};
32+
33+
layout(std140) uniform Uniforms2 {
34+
mat4 projectionMatrix;
35+
mat4 modelViewMatrix;
36+
mat3 normalMatrix;
37+
float one, two;
38+
} globals;
39+
40+
struct X {
41+
#if !defined(BLA)
42+
int y;
43+
#else
44+
float z;
45+
#endif
46+
};
47+
48+
struct LightData {
49+
float intensity;
50+
vec3 position;
51+
float one, two;
52+
};
53+
uniform LightData Light[4];
54+
55+
invariant pc_FragColor;
56+
57+
void main() {
58+
vec4 lightNormal = vec4(Light[0].position.xyz * Light[0].intensity, 0.0);
59+
vec4 clipPosition = projectionMatrix * modelViewMatrix * vec4(0, 0, 0, 1);
60+
vec4 clipPositionGlobals = globals.projectionMatrix * globals.modelViewMatrix * vec4(0, 0, 0, 1);
61+
if (false) {}
62+
pc_FragColor = vec4(texture(map, vUv).rgb, 0.0);
63+
float bar = 0.0;
64+
pc_FragColor.a += 1.0+bar;
65+
}

0 commit comments

Comments
 (0)