// ==UserScript==
// @name WebGL (Shaders优化工具)
// @namespace http://tampermonkey.net/
// @version 2.1.9
// @description 全栈WebGL性能优化(含WASM加速、智能LOD、动态批处理)
// @author KiwiFruit
// @match *://*/*
// @license MIT
// @grant GM.addStyle
// @grant GM.getValue
// @grant GM.setValue
// @grant GM.xmlHttpRequest
// @grant GM.getResourceUrl
// @connect wasm-optimizer.example.com
// @resource APIKEY https://yourdomain.com/apikey.txt
// @resource optimizer.wasm https://wasm-optimizer.example.com/optimizer.wasm
// ==/UserScript==
(function() {
'use strict';
/* global OctreeNode,options,DynamicBatcher */
// 核心常量
const CONSTANTS = {
MAX_TEXTURE_SIZE: 2048,
DEFAULT_LOD_LEVELS: 4,
WASM_CHUNK_SIZE: 512 * 1024,
PLUGIN_PREFIX: 'wgo_',
CORS_HEADERS: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + GM.getResourceText('APIKEY')
}
};
// WebGL上下文检测
function createContext(canvas, options) {
const versions = [
{ version: 2, context: 'webgl2', extensions: ['EXT_color_buffer_float'] },
{ version: 1, context: 'webgl', extensions: ['OES_texture_float'] }
];
for (const config of versions) {
try {
const gl = canvas.getContext(config.context, {
antialias: true,
depth: true,
stencil: true,
preserveDrawingBuffer: false
});
if (gl) {
return {
gl,
version: config.version,
extensions: getSupportedExtensions(gl, config.extensions)
};
}
} catch (e) {}
}
throw new Error('No WebGL context available');
}
// 扩展支持检测
function getSupportedExtensions(gl, required) {
return required.filter(ext => gl.getExtension(ext));
}
// 核心优化器类
class WebGLOptimizer {
constructor() {
this.config = this.loadConfig();
this.glContext = null;
this.wasmModule = null;
this.stats = {
frameTime: 0,
drawCalls: 0,
memoryUsage: 0,
lodChanges: 0
};
this.sceneGraph = null;
this.uiManager = null;
this.resourceLoader = null;
this.extensionManager = null;
}
// 配置加载系统
async loadConfig() {
const defaults = {
lodStrategy: 'distance',
batchThreshold: 50,
textureQuality: 0.75,
maxDrawDistance: 1000,
pluginEnabled: []
};
return {
...defaults,
...(await GM.getValue('webgl_config', defaults))
};
}
// WASM加载优化
async loadWASM() {
const importObject = {
env: {
memory: new WebAssembly.Memory({ initial: 256 }),
_log: console.log.bind(console),
_error: console.error.bind(console)
}
};
const wasmCode = await GM.getResourceText('optimizer.wasm');
this.wasmModule = await WebAssembly.instantiate(
new Uint8Array(wasmCode),
importObject
);
}
// 性能监控
initPerformanceMonitor() {
this.statsMonitor = new PerformanceMonitor({
updateFrequency: 30,
metrics: ['fps', 'memory', 'drawcalls'],
uiContainer: document.createElement('div')
});
// 内存泄漏检测
setInterval(() => {
const used = performance.memory.usedJSHeapSize;
const limit = performance.memory.jsHeapSizeLimit;
if (used > limit * 0.85) {
this.triggerGC();
}
}, 60000);
}
// 资源加载系统
async initResourceLoader() {
this.resourceLoader = new ResourceLoader({
maxConcurrency: navigator.hardwareConcurrency || 4,
cacheStrategy: 'lru',
maxCacheSize: 1024 * 1024 * 128 // 128MB
});
// 预加载核心资源
await Promise.all([
this.resourceLoader.loadShader('vertex', '/shaders/vert.glsl'),
this.resourceLoader.loadShader('fragment', '/shaders/frag.glsl'),
this.resourceLoader.loadModel('octree', '/models/octree.gltf')
]);
}
// 场景优化核心
optimizeScene(sceneData) {
const optimized = {
meshes: this.applyBatching(sceneData.meshes),
textures: this.optimizeTextures(sceneData.textures),
lights: this.optimizeLights(sceneData.lights),
camera: sceneData.camera
};
// 空间分割优化
this.sceneGraph = new OctreeSpacePartitioning({
root: optimized.meshes,
maxDepth: this.config.lodLevels,
splitThreshold: 8
});
return optimized;
}
// 动态批处理算法
applyBatching(meshes) {
const batches = new DynamicBatcher({
maxVertices: 65536,
maxIndices: 32768,
materialThreshold: 0.95
}).processMeshes(meshes);
// 批处理优化统计
this.stats.drawCalls = meshes.length;
this.stats.lodChanges = 0;
return batches;
}
// 纹理优化系统
optimizeTextures(textures) {
return textures.map(tex => ({
...tex,
mipmaps: this.generateMipmaps(tex.data),
format: this.selectFormat(tex.type),
compression: this.config.textureQuality > 0.8 ? 'BC7' : 'BC3'
}));
}
// 格式选择算法
selectFormat(type) {
const formatMap = {
'float': WebGL2RenderingContext.RG32F,
'int': WebGL2RenderingContext.RGBA8UI,
'uint': WebGL2RenderingContext.RGBA8UI,
'default': WebGL2RenderingContext.RGBA
};
return formatMap[type] || formatMap.default;
}
// 错误处理系统
handleError(stage, error) {
const handler = this.errorHandlers[stage] || this.defaultErrorHandler;
handler(error);
// 严重错误处理
if (error.severity === 'critical') {
this.triggerFallbackMode();
}
}
// 回退模式
triggerFallbackMode() {
this.config.lodStrategy = 'none';
this.config.batchThreshold = 20;
this.resourceLoader.clearCache();
this.statsMonitor.triggerWarning('Fallback mode activated');
}
// 插件系统
loadPlugin(name) {
const plugin = GM.getResourceText(`${CONSTANTS.PLUGIN_PREFIX}${name}`);
const module = new Function('return ' + plugin)();
return new module.PluginAPI(this);
}
// 内存管理
async releaseResources() {
if (this.wasmModule) {
this.wasmModule.instance.exports._free();
this.wasmModule = null;
}
this.resourceLoader.clearCache();
this.glContext.gl?.deleteProgram(this.shaderProgram);
}
}
// 性能监控组件
class PerformanceMonitor {
constructor(options) {
this.options = options;
this.metrics = new Map();
this.uiElement = document.createElement('div');
this.initUI();
}
initUI() {
this.uiElement.style.cssText = `
position: fixed;
top: 10px;
right: 10px;
background: rgba(0,0,0,0.8);
color: white;
padding: 10px;
border-radius: 5px;
z-index: 9999;
`;
document.body.appendChild(this.uiElement);
}
updateMetrics() {
const now = performance.now();
const frameTime = (now - this.lastFrame) || 16;
this.metrics.set('fps', 1000 / frameTime);
// 内存采样
const mem = performance.memory;
this.metrics.set('memory', (mem.usedJSHeapSize / 1024 / 1024).toFixed(1));
// 绘制调用统计
this.metrics.set('drawcalls', this.glContext?.gl?.getParameter(WebGL2RenderingContext.DRAW_CALLS) || 0);
this.lastFrame = now;
this.renderUI();
}
renderUI() {
let html = '<div style="font-family: monospace">';
this.metrics.forEach((value, key) => {
html += `<div>${key}: ${value}</div>`;
});
html += '</div>';
this.uiElement.innerHTML = html;
}
}
// 资源加载器
class ResourceLoader {
constructor(options) {
this.options = options;
this.queue = [];
this.activeLoads = 0;
this.cache = new Map();
}
async loadShader(type, url) {
const response = await fetch(url);
const shaderSource = await response.text();
return this.compileShader(type, shaderSource);
}
compileShader(type, source) {
const gl = this.optimizer.glContext.gl;
const shader = gl.createShader(type === 'vertex' ?
gl.VERTEX_SHADER : gl.FRAGMENT_SHADER);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
throw new Error(gl.getShaderInfoLog(shader));
}
return shader;
}
// 批量加载
async loadBatch(urls) {
return Promise.all(urls.map(url =>
fetch(url).then(res => res.arrayBuffer())
));
}
}
// 空间分割系统
class OctreeSpacePartitioning {
constructor(options) {
this.root = this.buildOctree(options.root, 0, options.maxDepth);
this.bounds = this.calculateBounds(options.root);
}
buildOctree(nodes, depth, maxDepth) {
if (depth >= maxDepth || nodes.length < options.splitThreshold) {
return new OctreeNode(nodes, this.bounds);
}
const center = this.calculateCenter(this.bounds);
const children = [];
for (let i = 0; i < 8; i++) {
const childBounds = this.createChildBounds(center, i);
const childNodes = nodes.filter(node =>
this.isInsideBounds(node.position, childBounds)
);
children.push(this.buildOctree(childNodes, depth + 1, maxDepth));
}
return new OctreeNode(children, this.bounds);
}
}
// 初始化流程
const canvas = document.createElement('canvas');
const optimizer = new WebGLOptimizer();
// 异步初始化
(async () => {
try {
const context = createContext(canvas, {
version: 2,
extensions: ['EXT_color_buffer_float']
});
optimizer.glContext = context;
await optimizer.loadWASM();
await optimizer.initResourceLoader();
optimizer.initPerformanceMonitor();
// 事件监听
window.addEventListener('resize', () => {
optimizer.glContext.gl.viewport(0, 0, canvas.width, canvas.height);
});
// 首次场景渲染
const sampleScene = await fetch('/sample_scene.json').then(res => res.json());
optimizer.optimizeScene(sampleScene);
} catch (error) {
optimizer.handleError('init', error);
}
})();
})();