matdbg: UI refresh (#7301)

This commit is contained in:
Powei Feng
2023-10-26 14:14:01 -07:00
committed by GitHub
parent e674420e9c
commit 9ccb8fce31
8 changed files with 66 additions and 799 deletions

View File

@@ -41,8 +41,8 @@ set(SRCS
set(RESOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR})
set(RESOURCE_BINS
${CMAKE_CURRENT_SOURCE_DIR}/web/style.css
${CMAKE_CURRENT_SOURCE_DIR}/web/script.js
${CMAKE_CURRENT_SOURCE_DIR}/web/api.js
${CMAKE_CURRENT_SOURCE_DIR}/web/app.js
${CMAKE_CURRENT_SOURCE_DIR}/web/index.html
)

View File

@@ -50,13 +50,28 @@ using utils::FixedCapacityVector;
// serves files directly from the source code tree.
#define SERVE_FROM_SOURCE_TREE 0
// When set to 1, we will serve an experimental frontend, which will potentially replace the current
// frontend when ready.
#define EXPERIMENTAL_WEB_FRAMEWORK 0
#if SERVE_FROM_SOURCE_TREE
namespace {
std::string const BASE_URL = "libs/matdbg/web";
} // anonymous
#else
#if !SERVE_FROM_SOURCE_TREE
#include "matdbg_resources.h"
#endif
#include <unordered_map>
namespace {
struct Asset {
std::string_view mime;
std::string_view data;
};
std::unordered_map<std::string_view, Asset> ASSET_MAP;
} // anonymous
#endif // SERVE_FROM_SOURCE_TREE
namespace filament::matdbg {
@@ -74,14 +89,6 @@ std::string_view const DebugServer::kErrorHeader =
"HTTP/1.1 404 Not Found\r\nContent-Type: %s\r\n"
"Connection: close\r\n\r\n";
#if EXPERIMENTAL_WEB_FRAMEWORK
namespace {
std::string const BASE_URL = "libs/matdbg/web/experiment";
} // anonymous
class FileRequestHandler : public CivetHandler {
public:
FileRequestHandler(DebugServer* server) : mServer(server) {}
@@ -92,66 +99,44 @@ public:
if (uri == "/") {
uri = "/index.html";
}
#if SERVE_FROM_SOURCE_TREE
if (uri == "/index.html" || uri == "/app.js" || uri == "/api.js") {
mg_send_file(conn, (BASE_URL + uri).c_str());
return true;
}
slog.e << "DebugServer: bad request at line " << __LINE__ << ": " << uri << io::endl;
return false;
}
private:
DebugServer* mServer;
};
#else
class FileRequestHandler : public CivetHandler {
public:
FileRequestHandler(DebugServer* server) : mServer(server) {}
bool handleGet(CivetServer *server, struct mg_connection *conn) {
auto const& kSuccessHeader = DebugServer::kSuccessHeader;
const struct mg_request_info* request = mg_get_request_info(conn);
std::string uri(request->request_uri);
if (uri == "/" || uri == "/index.html") {
#if SERVE_FROM_SOURCE_TREE
mg_send_file(conn, "libs/matdbg/web/index.html");
#else
mg_printf(conn, kSuccessHeader.data(), "text/html");
mg_write(conn, mServer->mHtml.c_str(), mServer->mHtml.size());
#endif
return true;
}
if (uri == "/style.css") {
#if SERVE_FROM_SOURCE_TREE
mg_send_file(conn, "libs/matdbg/web/style.css");
#else
mg_printf(conn, kSuccessHeader.data(), "text/css");
mg_write(conn, mServer->mCss.c_str(), mServer->mCss.size());
#endif
return true;
}
if (uri == "/script.js") {
#if SERVE_FROM_SOURCE_TREE
mg_send_file(conn, "libs/matdbg/web/script.js");
#else
mg_printf(conn, kSuccessHeader.data(), "text/javascript");
mg_write(conn, mServer->mJavascript.c_str(), mServer->mJavascript.size());
#endif
auto const& asset_itr = ASSET_MAP.find(uri);
if (asset_itr != ASSET_MAP.end()) {
auto const& mime = asset_itr->second.mime;
auto const& data = asset_itr->second.data;
mg_printf(conn, kSuccessHeader.data(), mime.data());
mg_write(conn, data.data(), data.size());
return true;
}
#endif
slog.e << "DebugServer: bad request at line " << __LINE__ << ": " << uri << io::endl;
return false;
}
private:
DebugServer* mServer;
};
#endif
DebugServer::DebugServer(Backend backend, int port) : mBackend(backend) {
#if !SERVE_FROM_SOURCE_TREE
mHtml = CString((const char*) MATDBG_RESOURCES_INDEX_DATA, MATDBG_RESOURCES_INDEX_SIZE - 1);
mJavascript = CString((const char*) MATDBG_RESOURCES_SCRIPT_DATA, MATDBG_RESOURCES_SCRIPT_SIZE - 1);
mCss = CString((const char*) MATDBG_RESOURCES_STYLE_DATA, MATDBG_RESOURCES_STYLE_SIZE - 1);
ASSET_MAP["/index.html"] = {
.mime = "text/html",
.data = {(char const*) MATDBG_RESOURCES_INDEX_DATA},
};
ASSET_MAP["/app.js"] = {
.mime = "text/javascript",
.data = {(char const*) MATDBG_RESOURCES_APP_DATA},
};
ASSET_MAP["/api.js"] = {
.mime = "text/javascript",
.data = {(char const*) MATDBG_RESOURCES_API_DATA},
};
#endif
// By default the server spawns 50 threads so we override this to 10. According to the civetweb

View File

@@ -242,6 +242,10 @@ class CodeViewer extends LitElement {
}
_rebuild() {
if (!this.active || !this.modified) {
console.log('Called rebuild while variant is inactive or unmodified');
return;
}
this.dispatchEvent(new CustomEvent(
'rebuild-shader',
{detail: this.editor.getValue(), bubbles: true, composed: true}

View File

@@ -1,23 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Filament Debugger</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
<link href="https://google.github.io/filament/favicon.png" rel="icon" type="image/x-icon" />
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700" rel="stylesheet">
<style>
html, body {
height: 100%;
margin: 0;
font-family: "Open Sans";
}
</style>
<script src="api.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.25.2/min/vs/loader.js"></script>
</head>
<body>
<script src="app.js" type="module"></script>
<matdbg-viewer></matdbg-viewer>
</body>
</html>

View File

@@ -1,96 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<head>
<title>Filament Debugger</title>
<meta charset="utf-8">
<link rel="icon" type="image/png" href="https://google.github.io/filament/favicon.png" />
<link href="https://fonts.googleapis.com/css?family=Lexend Deca" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body class="vbox viewport">
<header>matdbg</header>
<section class="main hbox space-between">
<nav class="vbox">
<div id="material-list" class="scrollable squishy">
</div>
<div id="material-detail" class="scrollable stretchy">
</div>
</nav>
<article id="shader-source">
</article>
</section>
<footer>&nbsp;</footer>
<template id="material-list-template">
<div style="white-space: nowrap">
{{#item}}
{{#is_label}}
<br/><b>{{label}}</b>
{{/is_label}}
{{#is_material}}
<a class="material {{classes}}" data-matid="{{matid}}">{{name}}</a>
{{/is_material}}
<br/>
{{/item}}
</div>
</template>
<template id="material-detail-template">
<div style="white-space: pre">
name = {{ name }}
version = {{ version }}
{{#required_attributes.length}}
<b>Required attributes</b>
{{#required_attributes}}
{{.}}
{{/required_attributes}}
{{/required_attributes.length}}
<b>OpenGL shaders</b>
{{#opengl}}
<a class="shader {{classes}}" data-glindex="{{index}}">{{index}} {{shaderModel}} {{pipelineStage}} {{variantString}}</a>
{{/opengl}}
<b>Vulkan shaders</b>
{{#vulkan}}
<a class="shader {{classes}}" data-vkindex="{{index}}">{{index}} {{shaderModel}} {{pipelineStage}} {{variantString}}</a>
{{/vulkan}}
<b>Metal shaders</b>
{{#metal}}
<a class="shader {{classes}}" data-metalindex="{{index}}">{{index}} {{shaderModel}} {{pipelineStage}} {{variantString}}</a>
{{/metal}}
<b>Material details</b>
{{#shading}}
shading model = {{model}}
vertex domain = {{vertex_domain}}
interpolation = {{interpolation}}
shadow multiply = {{shadow_multiply}}
specular antialiasing = {{specular_antialiasing}}
variance = {{variance}}
threshold = {{threshold}}
clear coat IOR change = {{clear_coat_IOR_change}}
{{/shading}}
{{#raster}}
blending = {{blending}}
mask threshold = {{mask_threshold}}
color write = {{color_write}}
depth write = {{depth_write}}
depth test = {{depth_test}}
double sided = {{double_sided}}
culling = {{culling}}
transparency = {{transparency}}
{{/raster}}
</div>
</template>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/3.0.1/mustache.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.17.1/min/vs/loader.js"></script>
<script src="script.js"></script>
</body>
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
<link href="https://google.github.io/filament/favicon.png" rel="icon" type="image/x-icon" />
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700" rel="stylesheet">
<style>
html, body {
height: 100%;
margin: 0;
font-family: "Open Sans";
}
</style>
<script src="api.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.25.2/min/vs/loader.js"></script>
</head>
<body>
<script src="app.js" type="module"></script>
<matdbg-viewer></matdbg-viewer>
</body>
</html>

View File

@@ -1,516 +0,0 @@
/*
* Copyright (C) 2019 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.
*/
const kMonacoBaseUrl = 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.25.2/min/';
const kUntitledPlaceholder = "untitled";
const materialList = document.getElementById("material-list");
const materialDetail = document.getElementById("material-detail");
const header = document.querySelector("header");
const footer = document.querySelector("footer");
const shaderSource = document.getElementById("shader-source");
const matDetailTemplate = document.getElementById("material-detail-template");
const matListTemplate = document.getElementById("material-list-template");
const STATUS_LOOP_TIMEOUT = 3000;
const gMaterialDatabase = {};
let gEditor = null;
let gCurrentMaterial = "00000000";
let gCurrentLanguage = "glsl";
let gCurrentShader = { matid: "00000000", glindex: 0 };
let gEditorIsLoading = false;
require.config({ paths: { "vs": `${kMonacoBaseUrl}vs` }});
window.MonacoEnvironment = {
getWorkerUrl: function() {
return `data:text/javascript;charset=utf-8,${encodeURIComponent(`
self.MonacoEnvironment = {
baseUrl: '${kMonacoBaseUrl}'
};
importScripts('${kMonacoBaseUrl}vs/base/worker/workerMain.js');`
)}`;
}
};
function getShaderAPI(selection) {
if (!selection) {
selection = gCurrentShader;
}
if ("glindex" in selection) return "opengl";
if ("vkindex" in selection) return "vulkan";
if ("metalindex" in selection) return "metal";
return "error";
}
function rebuildMaterial() {
let api = 0, index = -1;
const shader = getShaderRecord(gCurrentShader);
const shaderApi = getShaderAPI();
switch (shaderApi) {
case "opengl": api = 1; index = gCurrentShader.glindex; break;
case "vulkan": api = 2; index = gCurrentShader.vkindex; break;
case "metal": api = 3; index = gCurrentShader.metalindex; break;
}
if (shaderApi === "vulkan") {
if (gCurrentLanguage === "glsl") {
delete shader["spirv"];
} else if (gCurrentLanguage === "spirv") {
delete shader["glsl"];
}
}
const editedText = shader[gCurrentLanguage];
const req = new XMLHttpRequest();
req.open('POST', '/api/edit');
req.send(`${gCurrentShader.matid} ${api} ${index} ${editedText}`);
}
document.querySelector("body").addEventListener("click", (evt) => {
const anchor = evt.target.closest("a");
if (!anchor) {
return;
}
// Handle selection of a material.
if (anchor.classList.contains("material")) {
selectMaterial(anchor.dataset.matid, true);
return;
}
// Handle selection of a shader.
if (anchor.classList.contains("shader")) {
selectShader(anchor.dataset);
return;
}
// Handle a rebuild.
if (anchor.classList.contains("rebuild")) {
rebuildMaterial();
return;
}
// Handle language selection.
for (const lang of "glsl spirv msl".split(" ")) {
if (anchor.classList.contains(lang)) {
gCurrentLanguage = lang;
selectShader(gCurrentShader);
return;
}
}
});
// Handle Ctrl+Arrow for fast keyboard navigation between shader variants and materials. Either the
// materialStep or shaderStep argument can be non-zero (not both) and they must be -1, 0, or +1.
// TODO: this function could be vastly simplified by changing the format of the shader selector.
function selectNextShader(materialStep, shaderStep) {
if (materialStep !== 0) {
const matids = getDisplayedMaterials().map(m => m.matid).filter(m => m);
const currentIndex = matids.indexOf(gCurrentMaterial);
const nextIndex = currentIndex + materialStep;
if (nextIndex >= 0 && nextIndex < matids.length) {
selectMaterial(matids[nextIndex], true);
}
return;
}
const material = gMaterialDatabase[gCurrentMaterial];
const variants = [];
let currentIndex = 0;
for (const [index, shader] of material.opengl.entries()) {
if (index === gCurrentShader.glindex) currentIndex = variants.length;
variants.push({ matid, glindex: index });
}
for (const [index, shader] of material.vulkan.entries()) {
if (index === gCurrentShader.vkindex) currentIndex = variants.length;
variants.push({ matid, vkindex: index });
}
for (const [index, shader] of material.metal.entries()) {
if (index === gCurrentShader.metalindex) currentIndex = variants.length;
variants.push({ matid, metalindex: index });
}
const nextIndex = currentIndex + shaderStep;
if (nextIndex >= 0 && nextIndex < variants.length) {
selectShader(variants[nextIndex]);
}
}
function fetchMaterial(matid) {
fetch(`api/material?matid=${matid}`).then(function(response) {
return response.json();
}).then(function(matInfo) {
if (matid in gMaterialDatabase) {
return;
}
matInfo.matid = matid;
gMaterialDatabase[matid] = matInfo;
renderMaterialList();
});
}
function queryActiveShaders() {
if (!isConnected()) {
return;
}
fetch("api/active").then(function(response) {
return response.json();
}).then(function(activeMaterials) {
// The only active materials are the ones with active variants.
for (matid in gMaterialDatabase) {
const material = gMaterialDatabase[matid];
material.active = false;
}
for (matid in activeMaterials) {
const material = gMaterialDatabase[matid];
const activeBackend = activeMaterials[matid][0];
const activeShaders = activeMaterials[matid].slice(1);
for (const shader of material[activeBackend]) {
shader.active = activeShaders.indexOf(shader.variant) > -1;
material.active = material.active || shader.active;
}
}
renderMaterialList();
renderMaterialDetail();
})
.catch(error => {
// This can occur if the JSON is invalid.
console.error(error);
});
}
function isConnected() {
return footer.innerText == 'connected';
}
function onConnected() {
footer.innerText = 'connected';
fetch("api/matids").then(function(response) {
return response.json();
}).then(function(matInfo) {
for (matid of matInfo) {
if (!(matid in gMaterialDatabase)) {
fetchMaterial(matid);
}
}
});
}
function onDisconnected() {
footer.innerText = 'not connected';
for (matid in gMaterialDatabase) {
const material = gMaterialDatabase[matid];
material.active = false;
for (const shader of material.opengl) shader.active = false;
for (const shader of material.vulkan) shader.active = false;
for (const shader of material.metal) shader.active = false;
}
renderMaterialList();
renderMaterialDetail();
}
function statusLoop() {
// This is a hanging get except for when transition from disconnected to connected, which
// should return immediately.
fetch("api/status" + (isConnected() ? '' : '?firstTime'))
.then(async (response) => {
const matid = await response.text();
// A first-time request returned successfully
if (matid === '0') {
onConnected();
} else if (matid != '1') {
fetchMaterial(matid);
} // else matid == '1' and it's a no-op, we just loop again.
statusLoop();
})
.catch(err => {
onDisconnected();
setTimeout(statusLoop, STATUS_LOOP_TIMEOUT)
});
}
function fetchMaterials() {
fetch("api/materials").then(function(response) {
return response.json();
}).then(function(matJson) {
for (const matInfo of matJson) {
if (matInfo.matid in gMaterialDatabase) {
continue;
}
gMaterialDatabase[matInfo.matid] = matInfo;
}
selectMaterial(matJson[0].matid, true);
});
}
function fetchShader(selection, matinfo, onDone) {
let query, target, index;
switch (getShaderAPI(selection)) {
case "opengl":
index = parseInt(selection.glindex);
query = `type=${gCurrentLanguage}&glindex=${index}`;
target = matinfo.opengl[index];
break;
case "vulkan":
index = parseInt(selection.vkindex);
query = `type=${gCurrentLanguage}&vkindex=${index}`;
target = matinfo.vulkan[index];
break;
case "metal":
index = parseInt(selection.metalindex);
query = `type=${gCurrentLanguage}&metalindex=${index}`;
target = matinfo.metal[index];
break;
}
fetch(`api/shader?matid=${matinfo.matid}&${query}`).then(function(response) {
return response.text();
}).then(function(shaderText) {
target[gCurrentLanguage] = shaderText;
onDone();
});
}
function getDisplayedMaterials() {
const items = [];
// Names need not be unique, so we display a numeric suffix for non-unique names.
// To achieve stable ordering of anonymous materials, we first sort by matid.
const labels = new Set();
const matids = Object.keys(gMaterialDatabase).sort();
const duplicatedLabels = {};
for (const matid of matids) {
const name = gMaterialDatabase[matid].name || kUntitledPlaceholder;
if (labels.has(name)) {
duplicatedLabels[name] = 0;
} else {
labels.add(name);
}
}
// Build a list of objects to pass into the template string.
for (const matid of matids) {
const item = Object.assign({}, gMaterialDatabase[matid]);
item.classes = matid === gCurrentMaterial ? "current " : "";
if (!item.active) {
item.classes += "inactive "
}
item.domain = item.shading.material_domain === "surface" ? "surface" : "postpro";
item.is_material = true;
const name = item.name || kUntitledPlaceholder;
if (name in duplicatedLabels) {
const index = duplicatedLabels[name];
item.name = `${name} (${index})`;
duplicatedLabels[name] = index + 1;
} else {
item.name = name;
}
items.push(item);
}
// The template takes a flat list of items, so here we insert items for section headers using
// blank names, which causes them to sort to the top of their respective sections.
const sectionLabel = {"is_label": true, "name": ""};
items.push(Object.assign({"label": "Surface materials", "domain": "surface"}, sectionLabel));
items.push(Object.assign({"label": "PostProcess materials", "domain": "postpro"}, sectionLabel));
// Next, sort all materials and section headers.
items.sort((a, b) => {
if (a.domain > b.domain) return -1;
if (a.domain < b.domain) return +1;
if (a.name < b.name) return -1;
if (a.name > b.name) return +1;
return 0;
});
return items;
}
function renderMaterialList() {
const items = getDisplayedMaterials();
materialList.innerHTML = Mustache.render(matListTemplate.innerHTML, { "item": items } );
}
function updateClassList(array, indexProperty, selectedIndex) {
for (let item of array) {
const current = parseInt(item[indexProperty]) === selectedIndex;
item.classes = current ? "current " : "";
if (!item.active) {
item.classes += "inactive "
}
}
}
function renderMaterialDetail() {
const mat = gMaterialDatabase[gCurrentMaterial];
const ok = mat.matid === gCurrentShader.matid;
updateClassList(mat.opengl, "index", ok ? parseInt(gCurrentShader.glindex) : -1);
updateClassList(mat.vulkan, "index", ok ? parseInt(gCurrentShader.vkindex) : -1);
updateClassList(mat.metal, "index", ok ? parseInt(gCurrentShader.metalindex) : -1);
const item = Object.assign({}, mat);
if (item.shading.material_domain !== "surface") {
delete item.shading;
}
materialDetail.innerHTML = Mustache.render(matDetailTemplate.innerHTML, item);
}
function getShaderRecord(selection) {
const mat = gMaterialDatabase[gCurrentMaterial];
if (selection.glindex >= 0) return mat.opengl[parseInt(selection.glindex)];
if (selection.vkindex >= 0) return mat.vulkan[parseInt(selection.vkindex)];
if (selection.metalindex >= 0) return mat.metal[parseInt(selection.metalindex)];
return null;
}
function renderShaderStatus() {
const shader = getShaderRecord(gCurrentShader);
let statusString = "";
if (shader) {
const glsl = "glsl " + (gCurrentLanguage === "glsl" ? "active" : "");
const msl = "msl " + (gCurrentLanguage === "msl" ? "active" : "");
const spirv = "spirv " + (gCurrentLanguage === "spirv" ? "active" : "");
switch (getShaderAPI()) {
case "opengl":
statusString += ` &nbsp; <a class='status_button ${glsl}'>[GLSL]</a>`;
break;
case "metal":
statusString += ` &nbsp; <a class='status_button ${msl}'>[MSL]</a>`;
break;
case "vulkan":
statusString += ` &nbsp; <a class='status_button ${glsl}'>[GLSL]</a>`;
statusString += ` &nbsp; <a class='status_button ${spirv}'>[SPIRV]</a>`;
break;
}
if (shader.modified && gCurrentLanguage !== "spirv") {
statusString += " &nbsp; <a class='status_button rebuild'>[rebuild]</a>";
}
if (!shader.active) {
statusString += " &nbsp; <span class='warning'> selected variant is inactive </span>";
}
}
header.innerHTML = "matdbg" + statusString;
}
function selectShader(selection) {
const shader = getShaderRecord(selection);
if (!shader) {
console.error("Shader not yet available.")
return;
}
// Change the current language selection if necessary.
switch (getShaderAPI(selection)) {
case "opengl":
if (gCurrentLanguage !== "glsl") {
gCurrentLanguage = "glsl";
}
break;
case "vulkan":
if (gCurrentLanguage !== "spirv" && gCurrentLanguage !== "glsl") {
gCurrentLanguage = "spirv";
}
break;
case "metal":
if (gCurrentLanguage !== "msl") {
gCurrentLanguage = "msl";
}
break;
}
const showShaderSource = () => {
gCurrentShader = selection;
gCurrentShader.matid = gCurrentMaterial;
renderMaterialDetail();
gEditorIsLoading = true;
gEditor.setValue(shader[gCurrentLanguage]);
gEditorIsLoading = false;
shaderSource.style.visibility = "visible";
renderShaderStatus();
};
if (!shader[gCurrentLanguage]) {
const matInfo = gMaterialDatabase[gCurrentMaterial];
fetchShader(selection, matInfo, showShaderSource);
} else {
showShaderSource();
}
}
function onEdit(changes) {
if (gEditorIsLoading) {
return;
}
const shader = getShaderRecord(gCurrentShader);
if (!shader) {
return;
}
if (!shader.modified) {
shader.modified = true;
renderShaderStatus();
}
shader[gCurrentLanguage] = gEditor.getValue();
}
function selectMaterial(matid, selectFirstShader) {
gCurrentMaterial = matid;
renderMaterialList();
renderMaterialDetail();
if (selectFirstShader) {
const mat = gMaterialDatabase[gCurrentMaterial];
const selection = { matid };
if (mat.opengl.length > 0) selection.glindex = 0;
else if (mat.vulkan.length > 0) selection.vkindex = 0;
else if (mat.metal.length > 0) selection.metalindex = 0;
selectShader(selection);
}
}
function init() {
require(["vs/editor/editor.main"], function () {
const KeyMod = monaco.KeyMod, KeyCode = monaco.KeyCode;
gEditor = monaco.editor.create(shaderSource, {
value: "",
language: "cpp",
scrollBeyondLastLine: false,
readOnly: false,
minimap: { enabled: false }
});
gEditor.onDidChangeModelContent((e) => { onEdit(e.changes); });
gEditor.addCommand(KeyMod.CtrlCmd | KeyCode.KEY_S, () => rebuildMaterial());
gEditor.addCommand(KeyMod.Shift | KeyMod.WinCtrl | KeyCode.UpArrow, () => selectNextShader(-1, 0));
gEditor.addCommand(KeyMod.Shift | KeyMod.WinCtrl | KeyCode.DownArrow, () => selectNextShader(+1, 0));
gEditor.addCommand(KeyMod.Shift | KeyMod.WinCtrl | KeyCode.LeftArrow, () => selectNextShader(0, -1));
gEditor.addCommand(KeyMod.Shift | KeyMod.WinCtrl | KeyCode.RightArrow, () => selectNextShader(0, +1));
fetchMaterials();
});
Mustache.parse(matDetailTemplate.innerHTML);
Mustache.parse(matListTemplate.innerHTML);
statusLoop();
// Poll for active shaders once every second.
// Take care not to poll more frequently than the frame rate. Active variants are determined
// by the list of variants that were fetched between this query and the previous query.
setInterval(queryActiveShaders, 1000);
}
init();

View File

@@ -1,110 +0,0 @@
html, body, .viewport {
width: 100%;
height: 100%;
margin: 0;
}
body {
font-family: 'Lexend Deca', sans-serif;
overflow: hidden;
}
a, a:visited {
text-decoration: none;
color: #567;
cursor: pointer;
}
a:hover, a.current {
font-weight: bold;
color: #07f;
}
a.status_button {
color: #e4e682;
}
a.status_button.active {
color: #fff;
}
span.warning {
color: black;
background: orange;
font-size: small;
}
a.inactive {
color: #aaa;
}
a.inactive.current {
color: #5af;
}
pre {
font-family: Menlo, Monaco, "Courier New", monospace;
}
.vbox {
display: flex;
flex-direction: column;
}
.scrollable {
position: relative;
overflow-y: scroll;
}
.scrollable > * {
position: absolute;
}
.squishy {
flex-grow: 1;
}
.stretchy {
flex-grow: 1;
}
.hbox {
display: flex;
flex-direction: row;
}
.space-between {
justify-content: space-between;
}
header, footer {
height: 26px;
background: cornflowerblue;
padding: 5px 0 0 5px;
}
.main {
flex: 1;
}
article {
flex: 5;
border-top: solid 2px;
border-bottom: solid 2px;
visibility: hidden;
}
nav {
border: solid 2px;
font-size: 12px;
flex: 1;
}
nav > *:first-child {
border-bottom: solid 2px;
}
nav > * {
padding-left: 5px;
padding-right: 5px;
}