- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在我们的 WebGL 应用程序中,我试图在 Web Worker 中加载和解码纹理图像,以避免在主线程中渲染打嗝。在工作线程中使用 createImageBitmap 并将图像位图传回主线程效果很好,但在 Chrome 中这将使用 三 或更多(可能取决于内核数量?)单独的工作人员(ThreadPoolForegroundWorker)与主线程和我自己的工作人员一起将产生五个线程。
我猜这会导致我在四核上的剩余渲染干扰,因为我可以在 Chrome 的 DevTools 的性能功能中看到一些莫名其妙的长时间。
那么,我可以以某种方式限制 createImageBitmap 使用的 worker 数量吗?即使我将图像作为 blob 或数组缓冲区传输到主线程并从那里激活 createImageBitmap,它的工作人员也会与我自己的工作人员和主线程竞争。
我尝试在工作人员中创建常规图像,而不是在那里显式解码它们,但图像没有在工作人员上下文中定义,如果我想将它们创建为元素,文档也没有。并且常规图像也不能转移,因此在主线程上创建它们并将它们转移给工作人员似乎也不可行。
期待任何建议...
最佳答案
没有理由在工作人员中使用 createImageBitmap(好吧,见底部)。浏览器已经在单独的线程中解码图像。在 worker 身上做这件事不会给你任何东西。更大的问题是当您最终将图像传递给 WebGL 时,ImageBitmap 无法知道您将如何使用该图像。如果您要求的格式与 ImageBitmap 解码的格式不同,那么 WebGL 必须再次对其进行转换和/或解码,并且您无法为 ImageBitmap 提供足够的信息来告诉它您希望它解码的格式。
最重要的是,Chrome 中的 WebGL 必须将图像的数据从渲染进程传输到 GPU 进程,这对于大图像来说是一个相对较大的副本(RGBA 的 1024x1024 是 4meg)
一个更好的 API IMO 将允许您告诉 ImageBitmap 您想要什么格式以及您想要它在哪里(CPU、GPU)。这样,浏览器可以异步准备图像,并且在完成后不需要繁重的工作。
无论如何,这是一个测试。如果您取消选中“更新纹理”,那么它仍在下载和解码纹理,但它只是不调用 gl.texImage2D
上传纹理。在那种情况下,我看不到卡顿(不能证明这是问题所在,但这就是我认为的问题)
const m4 = twgl.m4;
const gl = document.querySelector('#webgl').getContext('webgl');
const ctx = document.querySelector('#graph').getContext('2d');
let update = true;
document.querySelector('#update').addEventListener('change', function() {
update = this.checked;
});
const vs = `
attribute vec4 position;
uniform mat4 matrix;
varying vec2 v_texcoord;
void main() {
gl_Position = matrix * position;
v_texcoord = position.xy;
}
`
const fs = `
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D tex;
void main() {
gl_FragColor = texture2D(tex, v_texcoord);
}
`;
const program = twgl.createProgram(gl, [vs, fs]);
const posLoc = gl.getAttribLocation(program, 'position');
const matLoc = gl.getUniformLocation(program, 'matrix');
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(posLoc);
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(
gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([0, 0, 255, 255]));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
const m = m4.identity();
let frameCount = 0;
let previousTime = 0;
let imgNdx = 0;
let imgAspect = 1;
const imageUrls = [
'https://i.imgur.com/KjUybBD.png',
'https://i.imgur.com/AyOufBk.jpg',
'https://i.imgur.com/UKBsvV0.jpg',
'https://i.imgur.com/TSiyiJv.jpg',
];
async function loadNextImage() {
const url = `${imageUrls[imgNdx]}?cachebust=${performance.now()}`;
imgNdx = (imgNdx + 1) % imageUrls.length;
const res = await fetch(url, {mode: 'cors'});
const blob = await res.blob();
const bitmap = await createImageBitmap(blob, {
premultiplyAlpha: 'none',
colorSpaceConversion: 'none',
});
if (update) {
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, bitmap);
imgAspect = bitmap.width / bitmap.height;
}
setTimeout(loadNextImage, 1000);
}
loadNextImage();
function render(currentTime) {
const deltaTime = currentTime - previousTime;
previousTime = currentTime;
{
const {width, height} = ctx.canvas;
const x = frameCount % width;
const y = 1000 / deltaTime / 60 * height / 2;
ctx.fillStyle = frameCount % (width * 2) < width ? 'red' : 'blue';
ctx.clearRect(x, 0, 1, height);
ctx.fillRect(x, y, 1, height);
ctx.clearRect(0, 0, 30, 15);
ctx.fillText((1000 / deltaTime).toFixed(1), 2, 10);
}
gl.useProgram(program);
const dispAspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
m4.scaling([1 / dispAspect, 1, 1], m);
m4.rotateZ(m, currentTime * 0.001, m);
m4.scale(m, [imgAspect, 1, 1], m);
m4.translate(m, [-0.5, -0.5, 0], m);
gl.uniformMatrix4fv(matLoc, false, m);
gl.drawArrays(gl.TRIANGLES, 0, 6);
++frameCount;
requestAnimationFrame(render);
}
requestAnimationFrame(render);
canvas { border: 1px solid black; margin: 2px; }
#ui { position: absolute; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<div id="ui"><input type="checkbox" id="update" checked><label for="update">Update Texture</label></div>
<canvas id="webgl"></canvas>
<canvas id="graph"></canvas>
gl.bufferSubData
将几行一帧上传到 WebGL .
const m4 = twgl.m4;
const gl = document.querySelector('#webgl').getContext('webgl');
const ctx = document.querySelector('#graph').getContext('2d');
const vs = `
attribute vec4 position;
uniform mat4 matrix;
varying vec2 v_texcoord;
void main() {
gl_Position = matrix * position;
v_texcoord = position.xy;
}
`
const fs = `
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D tex;
void main() {
gl_FragColor = texture2D(tex, v_texcoord);
}
`;
const program = twgl.createProgram(gl, [vs, fs]);
const posLoc = gl.getAttribLocation(program, 'position');
const matLoc = gl.getUniformLocation(program, 'matrix');
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(posLoc);
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
function createTexture(gl) {
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(
gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([0, 0, 255, 255]));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
return tex;
}
let drawingTex = createTexture(gl);
let loadingTex = createTexture(gl);
const m = m4.identity();
let frameCount = 0;
let previousTime = 0;
const workerScript = `
const ctx = new OffscreenCanvas(1, 1).getContext('2d');
let imgNdx = 0;
let imgAspect = 1;
const imageUrls = [
'https://i.imgur.com/KjUybBD.png',
'https://i.imgur.com/AyOufBk.jpg',
'https://i.imgur.com/UKBsvV0.jpg',
'https://i.imgur.com/TSiyiJv.jpg',
];
async function loadNextImage() {
const url = \`\${imageUrls[imgNdx]}?cachebust=\${performance.now()}\`;
imgNdx = (imgNdx + 1) % imageUrls.length;
const res = await fetch(url, {mode: 'cors'});
const blob = await res.blob();
const bitmap = await createImageBitmap(blob, {
premultiplyAlpha: 'none',
colorSpaceConversion: 'none',
});
ctx.canvas.width = bitmap.width;
ctx.canvas.height = bitmap.height;
ctx.drawImage(bitmap, 0, 0);
const imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
const data = new Uint8Array(imgData.data);
postMessage({
width: imgData.width,
height: imgData.height,
data: data.buffer,
}, [data.buffer]);
}
onmessage = loadNextImage;
`;
const blob = new Blob([workerScript], {type: 'application/javascript'});
const worker = new Worker(URL.createObjectURL(blob));
let imgAspect = 1;
worker.onmessage = async(e) => {
const {width, height, data} = e.data;
gl.bindTexture(gl.TEXTURE_2D, loadingTex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
const maxRows = 20;
for (let y = 0; y < height; y += maxRows) {
const rows = Math.min(maxRows, height - y);
gl.bindTexture(gl.TEXTURE_2D, loadingTex);
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, y, width, rows, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(data, y * width * 4, rows * width * 4));
await waitRAF();
}
const temp = loadingTex;
loadingTex = drawingTex;
drawingTex = temp;
imgAspect = width / height;
await waitMS(1000);
worker.postMessage('');
};
worker.postMessage('');
function waitRAF() {
return new Promise(resolve => requestAnimationFrame(resolve));
}
function waitMS(ms = 0) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function render(currentTime) {
const deltaTime = currentTime - previousTime;
previousTime = currentTime;
{
const {width, height} = ctx.canvas;
const x = frameCount % width;
const y = 1000 / deltaTime / 60 * height / 2;
ctx.fillStyle = frameCount % (width * 2) < width ? 'red' : 'blue';
ctx.clearRect(x, 0, 1, height);
ctx.fillRect(x, y, 1, height);
ctx.clearRect(0, 0, 30, 15);
ctx.fillText((1000 / deltaTime).toFixed(1), 2, 10);
}
gl.useProgram(program);
const dispAspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
m4.scaling([1 / dispAspect, 1, 1], m);
m4.rotateZ(m, currentTime * 0.001, m);
m4.scale(m, [imgAspect, 1, 1], m);
m4.translate(m, [-0.5, -0.5, 0], m);
gl.bindTexture(gl.TEXTURE_2D, drawingTex);
gl.uniformMatrix4fv(matLoc, false, m);
gl.drawArrays(gl.TRIANGLES, 0, 6);
++frameCount;
requestAnimationFrame(render);
}
requestAnimationFrame(render);
canvas { border: 1px solid black; margin: 2px; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas id="webgl"></canvas>
<canvas id="graph"></canvas>
const m4 = twgl.m4;
const gl = document.querySelector('#webgl').getContext('webgl');
const ctx = document.querySelector('#graph').getContext('2d');
const vs = `
attribute vec4 position;
uniform mat4 matrix;
varying vec2 v_texcoord;
void main() {
gl_Position = matrix * position;
v_texcoord = position.xy;
}
`
const fs = `
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D tex;
void main() {
gl_FragColor = texture2D(tex, v_texcoord);
}
`;
const program = twgl.createProgram(gl, [vs, fs]);
const posLoc = gl.getAttribLocation(program, 'position');
const matLoc = gl.getUniformLocation(program, 'matrix');
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(posLoc);
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
function createTexture(gl) {
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(
gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([0, 0, 255, 255]));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
return tex;
}
let drawingTex = createTexture(gl);
let loadingTex = createTexture(gl);
const m = m4.identity();
let frameCount = 0;
let previousTime = 0;
const workerScript = `
importScripts(
// from https://github.com/eugeneware/jpeg-js
'https://greggman.github.io/doodles/js/JPG-decoder.js',
// from https://github.com/photopea/UPNG.js
'https://greggman.github.io/doodles/js/UPNG.js',
);
let imgNdx = 0;
let imgAspect = 1;
const imageUrls = [
'https://i.imgur.com/KjUybBD.png',
'https://i.imgur.com/AyOufBk.jpg',
'https://i.imgur.com/UKBsvV0.jpg',
'https://i.imgur.com/TSiyiJv.jpg',
];
function decodePNG(arraybuffer) {
return UPNG.decode(arraybuffer)
}
function decodeJPG(arrayBuffer) {
return decode(new Uint8Array(arrayBuffer), true);
}
const decoders = {
'image/png': decodePNG,
'image/jpeg': decodeJPG,
'image/jpg': decodeJPG,
};
async function loadNextImage() {
const url = \`\${imageUrls[imgNdx]}?cachebust=\${performance.now()}\`;
imgNdx = (imgNdx + 1) % imageUrls.length;
const res = await fetch(url, {mode: 'cors'});
const arrayBuffer = await res.arrayBuffer();
const type = res.headers.get('Content-Type');
let decoder = decoders[type];
if (!decoder) {
console.error('unknown image type:', type);
}
const imgData = decoder(arrayBuffer);
postMessage({
width: imgData.width,
height: imgData.height,
arrayBuffer: imgData.data.buffer,
}, [imgData.data.buffer]);
}
onmessage = loadNextImage;
`;
const blob = new Blob([workerScript], {type: 'application/javascript'});
const worker = new Worker(URL.createObjectURL(blob));
let imgAspect = 1;
worker.onmessage = async(e) => {
const {width, height, arrayBuffer} = e.data;
gl.bindTexture(gl.TEXTURE_2D, loadingTex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
const maxRows = 20;
for (let y = 0; y < height; y += maxRows) {
const rows = Math.min(maxRows, height - y);
gl.bindTexture(gl.TEXTURE_2D, loadingTex);
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, y, width, rows, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(arrayBuffer, y * width * 4, rows * width * 4));
await waitRAF();
}
const temp = loadingTex;
loadingTex = drawingTex;
drawingTex = temp;
imgAspect = width / height;
await waitMS(1000);
worker.postMessage('');
};
worker.postMessage('');
function waitRAF() {
return new Promise(resolve => requestAnimationFrame(resolve));
}
function waitMS(ms = 0) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function render(currentTime) {
const deltaTime = currentTime - previousTime;
previousTime = currentTime;
{
const {width, height} = ctx.canvas;
const x = frameCount % width;
const y = 1000 / deltaTime / 60 * height / 2;
ctx.fillStyle = frameCount % (width * 2) < width ? 'red' : 'blue';
ctx.clearRect(x, 0, 1, height);
ctx.fillRect(x, y, 1, height);
ctx.clearRect(0, 0, 30, 15);
ctx.fillText((1000 / deltaTime).toFixed(1), 2, 10);
}
gl.useProgram(program);
const dispAspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
m4.scaling([1 / dispAspect, 1, 1], m);
m4.rotateZ(m, currentTime * 0.001, m);
m4.scale(m, [imgAspect, 1, 1], m);
m4.translate(m, [-0.5, -0.5, 0], m);
gl.bindTexture(gl.TEXTURE_2D, drawingTex);
gl.uniformMatrix4fv(matLoc, false, m);
gl.drawArrays(gl.TRIANGLES, 0, 6);
++frameCount;
requestAnimationFrame(render);
}
requestAnimationFrame(render);
canvas { border: 1px solid black; margin: 2px; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas id="webgl"></canvas>
<canvas id="graph"></canvas>
ImageBitmap
应该 足够快,我上面关于它没有足够信息的一些评论可能并不完全正确。
ImageBitmap
是为了快速上传。它应该通过你给它一个 blob 并异步地将该图像加载到 GPU 中来工作。当您调用
texImage2D
有了它,浏览器可以将该图像“blit”(使用 GPU 渲染)到您的纹理中。我不知道为什么在第一个示例中会出现卡顿,但我每隔 6 张左右的图像就会看到卡顿。
ImageBitmap
的点。 ,浏览器不需要上传到 GPU。
ImageBitmap
即使用户没有 GPU,它仍然应该可以工作。关键是由浏览器决定如何实现该功能以及它是快还是慢或无卡顿完全取决于浏览器。
关于javascript - 在网络 worker 中解码图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58856403/
这与 Payubiz payment gateway sdk 关系不大一体化。但是,主要问题与构建项目有关。 每当我们尝试在模拟器上运行应用程序时。我们得到以下失败: What went wrong:
我有一个现有的应用程序,其中包含在同一主机上运行的 4 个 docker 容器。它们已使用 link 命令链接在一起。 然而,在 docker 升级后,link 行为已被弃用,并且似乎有所改变。我们现
在 Internet 模型中有四层:链路 -> 网络 -> 传输 -> 应用程序。 我真的不知道网络层和传输层之间的区别。当我读到: Transport layer: include congesti
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
前言: 生活中,我们在上网时,打开一个网页,就可以看到网址,如下: https😕/xhuahua.blog.csdn.net/ 访问网站使用的协议类型:https(基于 http 实现的,只不过在
网络 避免网络问题降低Hadoop和HBase性能的最重要因素可能是所使用的交换硬件,在项目范围的早期做出的决策可能会导致群集大小增加一倍或三倍(或更多)时出现重大问题。 需要考虑的重要事项:
网络 网络峰值 如果您看到定期的网络峰值,您可能需要检查compactionQueues以查看主要压缩是否正在发生。 有关管理压缩的更多信息,请参阅管理压缩部分的内容。 Loopback IP
Pure Data 有一个 loadbang 组件,它按照它说的做:当图形开始运行时发送一个 bang。 NoFlo 的 core/Kick 在其 IN 输入被击中之前不会发送其数据,并且您无法在 n
我有一台 Linux 构建机器,我也安装了 minikube。在 minikube 实例中,我安装了 artifactory,我将使用它来存储各种构建工件 我现在希望能够在我的开发机器上做一些工作(这
我想知道每个视频需要多少种不同的格式才能支持所有主要设备? 在我考虑的主要设备中:安卓手机 + iPhone + iPad . 对具有不同比特率的视频进行编码也是一种好习惯吗? 那里有太多相互矛盾的信
我有一个使用 firebase 的 Flutter Web 应用程序,我有两个 firebase 项目(dev 和 prod)。 我想为这个项目设置 Flavors(只是网络没有移动)。 在移动端,我
我正在读这篇文章Ars article关于密码安全,它提到有一些网站“在传输之前对密码进行哈希处理”? 现在,假设这不使用 SSL 连接 (HTTPS),a.这真的安全吗? b.如果是的话,你会如何在
我试图了解以下之间的关系: eth0在主机上;和 docker0桥;和 eth0每个容器上的接口(interface) 据我了解,Docker: 创建一个 docker0桥接,然后为其分配一个与主机上
我需要编写一个java程序,通过网络将对象发送到客户端程序。问题是一些需要发送的对象是不可序列化的。如何最好地解决这个问题? 最佳答案 发送在客户端重建对象所需的数据。 关于java - 不可序列化对
所以我最近关注了this有关用 Java 制作基本聊天室的教程。它使用多线程,是一个“面向连接”的服务器。我想知道如何使用相同的 Sockets 和 ServerSockets 来发送对象的 3d 位
我想制作一个系统,其中java客户端程序将图像发送到中央服务器。中央服务器保存它们并运行使用这些图像的网站。 我应该如何发送图像以及如何接收它们?我可以使用同一个网络服务器来接收和显示网站吗? 最佳答
我正在尝试设置我的 rails 4 应用程序,以便它发送电子邮件。有谁知道我为什么会得到: Net::SMTPAuthenticationError 534-5.7.9 Application-spe
我正在尝试编写一个简单的客户端-服务器程序,它将客户端计算机连接到服务器计算机。 到目前为止,我的代码在本地主机上运行良好,但是当我将客户端代码中的 IP 地址替换为服务器计算机的本地 IP 地址时,
我需要在服务器上并行启动多个端口,并且所有服务器套接字都应在 socket.accept() 上阻塞。 同一个线程需要启动客户端套接字(许多)来连接到特定的 ServerSocket。 这能实现吗?
我的工作执行了大约 10000 次以下任务: 1) HTTP 请求(1 秒) 2)数据转换(0.3秒) 3)数据库插入(0.7秒) 每次迭代的总时间约为 2 秒,分布如上所述。 我想做多任务处理,但我
我是一名优秀的程序员,十分优秀!