Skip to content

Commit

Permalink
Support backend selector (#503)
Browse files Browse the repository at this point in the history
* Support backend selector

* Use dropdown instead of select

---------

Co-authored-by: Linchenn <[email protected]>
  • Loading branch information
axinging and Linchenn authored Jul 25, 2023
1 parent 7eb7289 commit 878e6fc
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 12 deletions.
34 changes: 34 additions & 0 deletions themes/tfjs/helper/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,26 @@ function isNonBrowserApiPage(path) {
isTflitePage(path) || isTaskApiPage(path);
}

// WebGPU is supported since version 4.6.0, WebGL is 2.0.0, Wasm is 1.7.0.
const firstWebgpuVersion = '4.6.0';
const firstWebglVersion = '2.0.0';
const firstWasmVersion = '1.7.0';
function isVersionSupported(version, oldVersion) {
const versionArray = version.split('.');
const oldVersionArray = oldVersion.split('.');
for (var i = 0; i < versionArray.length; i++) {
const a = ~~versionArray[i];
const b = ~~oldVersionArray[i];
if (a > b) {
return true
};
if (a < b) {
return false;
}
}
return true;
}

module.exports = function(hexo) {
return {
toJson: function(obj) {
Expand All @@ -82,6 +102,20 @@ module.exports = function(hexo) {

isNonBrowserApiPage: isNonBrowserApiPage,

isVersionSupported: isVersionSupported,

isWebGPUSupported: function(version) {
return isVersionSupported(version, firstWebgpuVersion);
},

isWebGLSupported: function(version) {
return isVersionSupported(version, firstWebglVersion);
},

isWasmSupported: function(version) {
return isVersionSupported(version, firstWasmVersion);
},

getApi: function(siteData, versionString, path) {
if (isApiVisPage(path)) {
return siteData[`api_vis/${versionString}/docs`];
Expand Down
7 changes: 6 additions & 1 deletion themes/tfjs/layout/layout.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,12 @@ limitations under the License.
<script src="{{url_for 'js/vendor/tf.min.js'}}"> </script>
{{else}}
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@{{page.title}}"> </script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-webgpu@latest"> </script>
{{#if (isWebGPUSupported page.title)}}
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-webgpu@{{page.title}}"> </script>
{{/if}}
{{#if (isWasmSupported page.title)}}
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@{{page.title}}/dist/tf-backend-wasm.js"> </script>
{{/if}}
{{/if}}
{{/if}}

Expand Down
76 changes: 74 additions & 2 deletions themes/tfjs/source/css/api.scss
Original file line number Diff line number Diff line change
Expand Up @@ -326,16 +326,17 @@ header {

.snippet-run-button {
position: absolute;
right: 4px;
right: 35px;
top: 5px;
font-size: 12px;
font-weight: bold;
}

.snippet-edit-button {
position: absolute;
right: 60px;
right: 90px;
top: 5px;
height: 20px;
font-size: 12px;
font-weight: bold;
}
Expand All @@ -350,5 +351,76 @@ header {
}
}

.snippet-backend-dropdown {
position: absolute;
right: 8px;
top: 5px;
height: 20px;
display: inline-block;

.snippet-backend-button {
color: #f4f4f4;
padding: 10px;
font-size: 16px;
border: none;
cursor: pointer;
}

.snippet-backend-button:hover,
.snippet-backend-button:focus {
background-color: #d4d4d4;
}

.snippet-backend-list {
display: none;
position: absolute;
background-color: #f1f1f1;
min-width: 103px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 1;

.snippet-ul {
list-style-type: none;
margin: 5px 5px 5px 12px;
padding: 0;

.li {
margin: 5px 5px 5px 12px;
padding: 0;
}
}
}

.snippet-show {
display: block;
}
}

/*From https://css.gg/chevron-down*/
.gg-chevron-down {
box-sizing: border-box;
position: relative;
display: block;
transform: scale(var(--ggs, 1));
width: 22px;
height: 22px;
border: 2px solid transparent;
}

.gg-chevron-down::after {
content: "";
display: block;
box-sizing: border-box;
position: absolute;
color: black;
width: 8px;
height: 8px;
border-bottom: 2px solid;
border-right: 2px solid;
transform: rotate(45deg);
left: 6.5px;
top: 5px
}

}
}
135 changes: 126 additions & 9 deletions themes/tfjs/source/js/codeSnippets.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ function getLineNumber(error) {
async function executeCodeSnippet(consoleLogElement, codeSnippet) {
consoleLogElement.innerText = '';
var oldLog = console.log;
console.log = function(...values) {
console.log = function (...values) {
let logStrs = [];
for (let i = 0; i < values.length; i++) {
const value = values[i];
Expand Down Expand Up @@ -86,7 +86,7 @@ async function executeCodeSnippet(consoleLogElement, codeSnippet) {
// It is important that codeSnippet and 'try {' be on the same line
// in order to not modify the line number on an error.
const evalString = '(async function runner() { try { ' + codeSnippet +
'\n} catch (e) { reportError(e); } })()';
'\n} catch (e) { reportError(e); } })()';

if (window._tfengine && window._tfengine.startScope) {
window._tfengine.startScope();
Expand All @@ -96,7 +96,7 @@ async function executeCodeSnippet(consoleLogElement, codeSnippet) {

// this outer try is for errors that prevent the snippet from being parsed.
try {
await eval(evalString).catch(function(e) {
await eval(evalString).catch(function (e) {
// This catch is for errors within promises within snippets
reportError(e);
});
Expand Down Expand Up @@ -131,12 +131,70 @@ function makeEditable(codeBlock) {
codeBlock.codeMirror = myCodeMirror;
}

function isVersionSupported(version, oldVersion) {
const versionArray = version.split('.');
const oldVersionArray = oldVersion.split('.');
for (var i = 0; i < versionArray.length; i++) {
const a = ~~versionArray[i];
const b = ~~oldVersionArray[i];
if (a > b) {
return true
};
if (a < b) {
return false;
}
}
return true;
}

function genBackendListString(version) {
const openLiStr = `<li><input type="radio" name='backend' value=`;
const closeLiStr = `</li>`;
var genLiStr = function (backendName) {
return `${openLiStr}"${backendName}">${backendName}${closeLiStr}`;
}
var genLiCheckedStr = function (backendName) {
return `${openLiStr}"${backendName}" checked="checked"/>${backendName}` +
`${closeLiStr}`;
}

// WebGPU is supported since version 4.6.0, WebGL is 2.0.0, Wasm is 1.7.0.
const firstWebgpuVersion = '4.6.0';
const firstWebglVersion = '2.0.0';
const firstWasmVersion = '1.7.0';
var backendListStr =
`<button class="snippet-backend-button gg-chevron-down">` +
`</button><div class="snippet-backend-list"><ul class="snippet-ul">`;
var backendName;
if (isVersionSupported(version, firstWebgpuVersion)) {
backendName = 'webgpu';
backendListStr += `${genLiCheckedStr(backendName)}` +
`${genLiStr('webgl')}${genLiStr('wasm')}${genLiStr('cpu')}`;
} else if (isVersionSupported(version, firstWebglVersion)) {
backendName = 'webgl';
backendListStr += `${genLiCheckedStr(backendName)}` +
`${genLiStr('wasm')}${genLiStr('cpu')}`;
} else if (isVersionSupported(version, firstWasmVersion)) {
backendName = 'wasm';
backendListStr += `${genLiCheckedStr(backendName)}${genLiStr('cpu')}`;
} else {
backendName = 'cpu';
backendListStr += `${genLiCheckedStr(backendName)}`;
}
backendListStr += `</ul></div>`;
return [backendName, backendListStr];
}

function initCodeBlocks(selector) {
// Find all the code blocks.
var jsBlocks =
Array.prototype.slice.call(document.querySelectorAll(selector));
Array.prototype.slice.call(document.querySelectorAll(selector));
const version =
document.querySelector('.mdc-select__selected-text').innerText;
const [backendName, backendListStr] = genBackendListString(version);
tf.setBackend(backendName);

jsBlocks.forEach(function(block) {
jsBlocks.forEach(function (block) {
var consoleElement = document.createElement('div');
consoleElement.className = 'snippet-console';

Expand All @@ -151,15 +209,53 @@ function initCodeBlocks(selector) {
var consoleLogElement = document.createElement('div');
consoleLogElement.className = 'snippet-console-log';

const consoleBackendSelectorElement = document.createElement('div');
consoleBackendSelectorElement.innerText = 'webgpu';
consoleBackendSelectorElement.className = 'snippet-backend-dropdown';
consoleBackendSelectorElement.innerHTML = backendListStr;

consoleElement.appendChild(consoleLogElement);
consoleElement.appendChild(consoleEditElement);
consoleElement.appendChild(consoleRunElement);

consoleElement.appendChild(consoleBackendSelectorElement);
block.parentElement.insertAdjacentElement('afterend', consoleElement);

consoleRunElement.addEventListener('click', async function() {
const backendDropdown =
consoleElement.querySelector('.snippet-backend-dropdown');
backendDropdown.querySelector('.snippet-backend-button.gg-chevron-down')
.onclick = function (evt) {
const lastBackendList = window.backendListGlobal;
window.backendListGlobal =
backendDropdown.querySelector('.snippet-backend-list');
// If there is any dropdown list open, close it first.
if (lastBackendList &&
!lastBackendList.isSameNode(window.backendListGlobal)) {
if (lastBackendList.classList.contains('snippet-show')) {
lastBackendList.classList.remove('snippet-show');
}
}

// Close or show current dropdown list.
if (window.backendListGlobal.classList.contains('snippet-show')) {
window.backendListGlobal.classList.remove('snippet-show');
window.backendListGlobal = null;
setBackend();
} else {
const backendButtons =
window.backendListGlobal.querySelectorAll('input[name="backend"]');
for (const backendButton of backendButtons) {
if (backendButton.value === tf.getBackend()) {
backendButton.checked = true;
break;
}
}
window.backendListGlobal.classList.add('snippet-show');
}
};

consoleRunElement.addEventListener('click', async function () {
var consoleLogElement =
this.parentElement.querySelector('.snippet-console-log');
this.parentElement.querySelector('.snippet-console-log');

var snippetText;
if (block.codeMirror) {
Expand All @@ -176,9 +272,30 @@ function initCodeBlocks(selector) {
executeCodeSnippet(consoleLogElement, snippetText);
});

consoleEditElement.addEventListener('click', function() {
consoleEditElement.addEventListener('click', function () {
makeEditable(block);
this.disabled = true;
});
});

function setBackend() {
const backendButtons = document.querySelectorAll('input[name="backend"]');
for (const backendButton of backendButtons) {
if (backendButton.checked) {
tf.setBackend(backendButton.value);
return;
}
}
}

window.onclick = function (event) {
// Close the dropdown list if the user clicks outside of it.
if (!event.target.matches('.snippet-backend-button')) {
if (window.backendListGlobal &&
window.backendListGlobal.classList.contains('snippet-show')) {
window.backendListGlobal.classList.remove('snippet-show');
setBackend();
}
}
}
}

0 comments on commit 878e6fc

Please sign in to comment.