/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // api.js encapsulates all of the REST endpoints that the server provides async function _fetchJson(uri) { const response = await fetch(uri); return await response.json(); } async function _fetchText(uri) { const response = await fetch(uri); return await response.text(); } async function fetchShaderCode(matid, backend, language, index) { let query; switch (backend) { case "opengl": case "essl1": query = `type=${language}&glindex=${index}`; break; case "vulkan": query = `type=${language}&vkindex=${index}`; break; case "metal": query = `type=${language}&metalindex=${index}`; break; case "webgpu": query = `type=${language}&wgpuindex=${index}`; break; } return await _fetchText(`api/shader?matid=${matid}&${query}`); } async function fetchMaterials() { const matJson = await _fetchJson("api/materials") const ret = {}; for (const matInfo of matJson) { ret[matInfo.matid] = matInfo; } return ret; } async function fetchMaterial(matId) { const matInfo = await _fetchJson(`api/material?matid=${matid}`); matInfo.matid = matid; return matInfo; } async function fetchMatIds() { const matInfo = await _fetchJson("api/matids"); const ret = []; for (matid of matInfo) { ret.push(matid); } return ret; } async function queryActiveShaders() { const activeVariants = await _fetchJson("api/active"); const actives = {}; for (matid in activeVariants) { const backend = activeVariants[matid][0]; const shaderModel = activeVariants[matid][1]; const variants = activeVariants[matid].slice(2); actives[matid] = { backend, shaderModel, variants }; } return actives; } function rebuildMaterial(materialId, backend, shaderIndex, editedText) { let api = 0; switch (backend) { case "opengl": case "essl1": api = 1; break; case "vulkan": api = 2; break; case "metal": api = 3; break; case "webgpu": api = 4; break; } return new Promise((ok, fail) => { const req = new XMLHttpRequest(); req.open('POST', '/api/edit'); req.send(`${materialId} ${api} ${shaderIndex} ${editedText}`); req.onload = ok; req.onerror = fail; }); } function activeShadersLoop(isConnected, onActiveShaders) { setInterval(async () => { if (isConnected()) { onActiveShaders(await queryActiveShaders()); } }, 1000); } const STATUS_LOOP_TIMEOUT = 3000; const STATUS_CONNECTED = 1; const STATUS_DISCONNECTED = 2; const STATUS_MATERIAL_UPDATED = 3; // Status function should be of the form function(status, data) async function statusLoop(isConnected, onStatus) { // This is a hanging get except for when transition from disconnected to connected, which // should return immediately. try { const matid = await _fetchText("api/status" + (isConnected() ? '' : '?firstTime')); // A first-time request returned successfully if (matid === '0') { onStatus(STATUS_CONNECTED); } else if (matid !== '1') { onStatus(STATUS_MATERIAL_UPDATED, matid); } // matid == '1' is no-op, just loop again statusLoop(isConnected, onStatus); } catch { onStatus(STATUS_DISCONNECTED); setTimeout(() => statusLoop(isConnected, onStatus), STATUS_LOOP_TIMEOUT) } } // Use browser User-agent to guess the current backend. This is mainly for matinfo which does // not have a running backend. function guessBackend() { const AGENTS_TO_BACKEND = [ ['Mac OS', 'metal'], ['Windows', 'opengl'], ['Linux', 'vulkan'], ]; const result = AGENTS_TO_BACKEND.filter((agent_backend) => { return window.navigator.userAgent.search(agent_backend[0]); }).map((agent_backend) => agent_backend[1]); return result.length > 0 ? result[0] : null; }