이미지 로딩, ImageBitmap, texture upload
이미지는 파일을 읽는 단계, decode 단계, GPU upload 단계가 다릅니다. editor asset pipeline은 이 단계를 분리해 관리해야 합니다.
decode와 upload를 분리하는 코드
이미지 로딩은 비동기 decode와 GPU upload를 따로 다룹니다.
async function loadImageBitmapFromBlob(blob) {
return await createImageBitmap(blob, {
colorSpaceConversion: "default",
premultiplyAlpha: "premultiply"
});
}
function uploadImageTexture(gl, bitmap) {
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_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);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, bitmap);
return texture;
}
pipeline
File/Blob
decode
ImageBitmap
Texture
GPU cache
원본 파일, decoded bitmap, GPU texture는 수명과 메모리 비용이 다릅니다.
upload queue로 프레임을 보호한다
function createTextureUploadQueue(gl, maxUploadsPerFrame = 2) {
const queue = [];
return {
enqueue(assetId, bitmap) {
queue.push({ assetId, bitmap });
},
flush(textureCache) {
let count = 0;
while (queue.length && count < maxUploadsPerFrame) {
const job = queue.shift();
textureCache.set(job.assetId, uploadImageTexture(gl, job.bitmap));
job.bitmap.close?.();
count += 1;
}
}
};
}
큰 이미지를 여러 개 동시에 업로드하면 drag 중 프레임이 튈 수 있습니다. decode는 비동기로 끝내고, GPU upload는 frame budget 안에서 나눠 처리합니다.
오늘의 핵심
이미지 노드는 단순한 URL이 아닙니다. decode와 upload를 분리해야 성능과 cache 정책을 만들 수 있습니다.