Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Prep for Texture refactor #1968

Merged
merged 2 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ module.exports = getESLintConfig({
'default-case': ['warn'],
'no-eq-null': ['warn'],
eqeqeq: ['warn'],
radix: 0
radix: 0,
'spaced-comment': ["error", "always", { "exceptions": ["/ <"] }]
// 'accessor-pairs': ['error', {getWithoutSet: false, setWithoutGet: false}]
},

Expand Down
16 changes: 8 additions & 8 deletions examples/api/cubemap/app.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {Device, loadImage, glsl} from '@luma.gl/core';
import {AnimationLoopTemplate, AnimationProps, CubeGeometry, Model, ModelProps} from '@luma.gl/engine';
import {GL} from '@luma.gl/constants';
import {Matrix4, radians} from '@math.gl/core';

const INFO_HTML = `
Expand Down Expand Up @@ -102,18 +101,19 @@ export default class AppAnimationLoopTemplate extends AnimationLoopTemplate {
const cubemap = device.createTexture({
dimension: 'cube',
mipmaps: true,
// @ts-ignore
data: {
[GL.TEXTURE_CUBE_MAP_POSITIVE_X]: loadImage('sky-posx.png'),
[GL.TEXTURE_CUBE_MAP_NEGATIVE_X]: loadImage('sky-negx.png'),
[GL.TEXTURE_CUBE_MAP_POSITIVE_Y]: loadImage('sky-posy.png'),
[GL.TEXTURE_CUBE_MAP_NEGATIVE_Y]: loadImage('sky-negy.png'),
[GL.TEXTURE_CUBE_MAP_POSITIVE_Z]: loadImage('sky-posz.png'),
[GL.TEXTURE_CUBE_MAP_NEGATIVE_Z]: loadImage('sky-negz.png')
'+X': loadImage('sky-posx.png'),
'-X': loadImage('sky-negx.png'),
'+Y': loadImage('sky-posy.png'),
'-Y': loadImage('sky-negy.png'),
'+Z': loadImage('sky-posz.png'),
'-Z': loadImage('sky-negz.png')
}
});

const texture = device.createTexture({
data: 'vis-logo.png',
data: loadImage('vis-logo.png'),
mipmaps: true,
sampler: {
magFilter: 'linear',
Expand Down
4 changes: 2 additions & 2 deletions examples/tutorials/hello-cube/app.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// luma.gl, MIT license
import {glsl, UniformStore, NumberArray, ShaderUniformType} from '@luma.gl/core';
import {glsl, UniformStore, NumberArray, ShaderUniformType, loadImage} from '@luma.gl/core';
import {AnimationLoopTemplate, AnimationProps, Model, CubeGeometry} from '@luma.gl/engine';
import {Matrix4} from '@math.gl/core';

Expand Down Expand Up @@ -70,7 +70,7 @@ export default class AppAnimationLoopTemplate extends AnimationLoopTemplate {
super();

const texture = device.createTexture({
data: 'vis-logo.png',
data: loadImage('vis-logo.png'),
mipmaps: true,
sampler: device.createSampler({
minFilter: 'linear',
Expand Down
5 changes: 2 additions & 3 deletions examples/tutorials/lighting/app.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {glsl, NumberArray} from '@luma.gl/core';
import {glsl, NumberArray, loadImage} from '@luma.gl/core';
import {AnimationLoopTemplate, AnimationProps, Model, CubeGeometry, _ShaderInputs} from '@luma.gl/engine';
import {phongMaterial, lighting, ShaderModule} from '@luma.gl/shadertools';
import {Matrix4} from '@math.gl/core';
Expand Down Expand Up @@ -75,7 +75,6 @@ const app: ShaderModule<AppUniforms, AppUniforms> = {
}
};


// APPLICATION

const eyePosition = [0, 0, 5];
Expand Down Expand Up @@ -112,7 +111,7 @@ export default class AppAnimationLoopTemplate extends AnimationLoopTemplate {
}
});

const texture = device.createTexture({data: 'vis-logo.png'});
const texture = device.createTexture({data: loadImage('vis-logo.png')});

this.model = new Model(device, {
vs,
Expand Down
2 changes: 2 additions & 0 deletions modules/constants/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export {GL} from './webgl-constants';

// WebGL types
export type {
GLTextureTarget,
GLTextureCubeMapTarget,
GLPrimitiveTopology,
GLPrimitive,
GLDataType,
Expand Down
21 changes: 19 additions & 2 deletions modules/constants/src/webgl-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type NumberArray = number[] | TypedArray;
export type NumericArray = TypedArray | number[];

/** TypeScript type covering all typed arrays */

export type TypedArray =
| Int8Array
| Uint8Array
Expand All @@ -27,6 +28,22 @@ export type TypedArray =
/** We don't know the type of Framebuffer at this stage */
type Framebuffer = unknown;

/** All possible texture targets */
export type GLTextureTarget =
| GL.TEXTURE_2D
| GL.TEXTURE_CUBE_MAP
| GL.TEXTURE_2D_ARRAY
| GL.TEXTURE_3D;

/** All possible cube face targets for textImage2D */
export type GLTextureCubeMapTarget =
| GL.TEXTURE_CUBE_MAP_POSITIVE_X
| GL.TEXTURE_CUBE_MAP_NEGATIVE_X
| GL.TEXTURE_CUBE_MAP_POSITIVE_Y
| GL.TEXTURE_CUBE_MAP_NEGATIVE_Y
| GL.TEXTURE_CUBE_MAP_POSITIVE_Z
| GL.TEXTURE_CUBE_MAP_NEGATIVE_Z;

/** Rendering primitives. Constants passed to drawElements() or drawArrays() to specify what kind of primitive to render. */
export type GLPrimitiveTopology =
| GL.POINTS
Expand All @@ -50,9 +67,9 @@ export type GLDataType =
| GL.SHORT
| GL.INT;

/** Pixel Type */
/** Pixel Data Type */
export type GLPixelType =
| GL.UNSIGNED_BYTE
| GLDataType
| GL.UNSIGNED_SHORT_5_6_5
| GL.UNSIGNED_SHORT_4_4_4_4
| GL.UNSIGNED_SHORT_5_5_5_1;
Expand Down
12 changes: 11 additions & 1 deletion modules/core-tests/test/adapter/device.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// luma.gl, MIT license
import test from 'tape-promise/tape';
import {getWebGLTestDevices} from '@luma.gl/test-utils';
import {getWebGLTestDevices, getTestDevices} from '@luma.gl/test-utils';

// import {luma} from '@luma.gl/core';

Expand All @@ -13,6 +13,16 @@ test('WebGLDevice#info', (t) => {
t.end();
});

// Minimal test, extensive test in texture-formats.spec
test('WebGLDevice#isTextureFormatCompressed', async (t) => {
for (const device of await getTestDevices()) {
// Just sanity check two types
t.equal(device.isTextureFormatCompressed('rgba8unorm'), false);
t.equal(device.isTextureFormatCompressed('bc3-rgba-unorm'), true)
}
t.end();
});

test('WebGLDevice#lost (Promise)', async (t) => {
// const device = await luma.createDevice({webgl2: false});

Expand Down
18 changes: 12 additions & 6 deletions modules/core-tests/test/adapter/resources/texture.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,18 +132,24 @@ function testFormatDeduction(t, device: Device) {
}
}

test.skip('WebGL#Texture format deduction', t => {
testFormatDeduction(t, webglDevice);
test.skip('WebGL#Texture format deduction', async (t) => {
for (const device of await getTestDevices()) {
testFormatDeduction(t, device);
}
t.end();
});

test.skip('WebGL#Texture format creation', t => {
testFormatCreation(t, webglDevice);
test.skip('WebGL#Texture format creation', async (t) => {
for (const device of await getTestDevices()) {
testFormatCreation(t, device);
}
t.end();
});

test.skip('WebGL#Texture format creation with data', t => {
testFormatCreation(t, webglDevice, true);
test.skip('WebGL#Texture format creation with data', async (t) => {
for (const device of await getTestDevices()) {
testFormatCreation(t, device, true);
}
t.end();
});

Expand Down
13 changes: 13 additions & 0 deletions modules/core-tests/test/adapter/texture-formats.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import test from 'tape-promise/tape';
import {getTestDevices} from '@luma.gl/test-utils';

// import {luma} from '@luma.gl/core';

// TODO - add full reference table, more exhaustive test
test('WebGLDevice#isTextureFormatCompressed', async (t) => {
for (const device of await getTestDevices()) {
t.equal(device.isTextureFormatCompressed('rgba8unorm'), false);
t.equal(device.isTextureFormatCompressed('bc3-rgba-unorm'), true)
}
t.end();
});
3 changes: 3 additions & 0 deletions modules/core-tests/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import './adapter/device-helpers/set-device-parameters.spec';
// import './adapter/webgl-canvas-context.spec';

// Resources
import './adapter/texture-formats.spec';

// Resources - TODO these tests only depend on Device and could move to API...
import './adapter/resources/buffer.spec';
import './adapter/resources/command-buffer.spec';
import './adapter/resources/framebuffer.spec';
Expand Down
33 changes: 25 additions & 8 deletions modules/core/src/adapter/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import type {CommandEncoder, CommandEncoderProps} from './resources/command-enco
import type {VertexArray, VertexArrayProps} from './resources/vertex-array';
import type {TransformFeedback, TransformFeedbackProps} from './resources/transform-feedback';

import {isTextureFormatCompressed} from './type-utils/decode-texture-format';

/**
* Identifies the GPU vendor and driver.
* @note Chrome WebGPU does not provide much information, though more can be enabled with
Expand Down Expand Up @@ -105,7 +107,7 @@ export abstract class DeviceLimits {
abstract maxComputeWorkgroupSizeZ: number;
/** max ComputeWorkgroupsPerDimension */
abstract maxComputeWorkgroupsPerDimension: number;
};
}

/** Set-like class for features (lets apps check for WebGL / WebGPU extensions) */
export class DeviceFeatures {
Expand All @@ -130,7 +132,6 @@ export type DeviceFeature =
| WebGLDeviceFeature
| WebGLCompressedTextureFeatures;


export type WebGPUDeviceFeature =
| 'depth-clip-control'
| 'indirect-first-instance'
Expand All @@ -144,8 +145,8 @@ export type WebGPUDeviceFeature =
| 'texture-compression-bc'
| 'texture-compression-etc2'
| 'texture-compression-astc';
// | 'depth-clamping' // removed from the WebGPU spec...
// | 'pipeline-statistics-query' // removed from the WebGPU spec...
// | 'depth-clamping' // removed from the WebGPU spec...
// | 'pipeline-statistics-query' // removed from the WebGPU spec...

export type WebGLDeviceFeature =
// webgl extension features
Expand All @@ -156,7 +157,7 @@ export type WebGLDeviceFeature =

// GLSL extension features
| 'shader-noperspective-interpolation-webgl' // Vertex outputs & fragment inputs can have a `noperspective` interpolation qualifier.
| 'shader-conservative-depth-webgl' // GLSL `gl_FragDepth` qualifiers `depth_unchanged` etc can enable early depth test
| 'shader-conservative-depth-webgl' // GLSL `gl_FragDepth` qualifiers `depth_unchanged` etc can enable early depth test
| 'shader-clip-cull-distance-webgl' // Makes gl_ClipDistance and gl_CullDistance available in shaders

// texture rendering
Expand Down Expand Up @@ -202,8 +203,9 @@ export type DeviceProps = {
// preserveDrawingBuffer?: boolean; // Default render target buffers will not be automatically cleared and will preserve their values until cleared or overwritten
// failIfMajorPerformanceCaveat?: boolean; // Do not create if the system performance is low.

onError?: (error: Error) => unknown;
/** Instrument context (at the expense of performance) */
debug?: boolean;
debug?: boolean;
/** Initialize the SpectorJS WebGL debugger */
spector?: boolean;

Expand Down Expand Up @@ -239,7 +241,10 @@ export abstract class Device {
// preserveDrawingBuffer: undefined,
// failIfMajorPerformanceCaveat: undefined

gl: null
gl: null,

// Callbacks
onError: (error: Error) => log.error(error.message)
};

get [Symbol.toStringTag](): string {
Expand Down Expand Up @@ -286,6 +291,11 @@ export abstract class Device {
/** Check if device supports rendering to a specific texture format */
abstract isTextureFormatRenderable(format: TextureFormat): boolean;

/** Check if a specific texture format is GPU compressed */
isTextureFormatCompressed(format: TextureFormat): boolean {
return isTextureFormatCompressed(format);
}

// Device loss

/** `true` if device is already lost */
Expand Down Expand Up @@ -437,7 +447,14 @@ export abstract class Device {
throw new Error('not implemented');
}

// Implementation
// IMPLEMENTATION

// Error Handling

/** Report unhandled device errors */
onError(error: Error) {
this.props.onError(error);
}

protected _getBufferProps(props: BufferProps | ArrayBuffer | ArrayBufferView): BufferProps {
if (props instanceof ArrayBuffer || ArrayBuffer.isView(props)) {
Expand Down
13 changes: 13 additions & 0 deletions modules/core/src/adapter/type-utils/decode-texture-format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ import {TextureFormat} from '../types/texture-formats';
import {VertexType} from '../types/vertex-formats';
import {decodeVertexType} from './decode-data-type';


// prettier-ignore
const COMPRESSED_TEXTURE_FORMAT_PREFIXES = [
'bc1', 'bc2', 'bc3', 'bc4', 'bc5', 'bc6', 'bc7', 'etc1', 'etc2', 'eac', 'atc', 'astc', 'pvrtc'
];

const REGEX = /^(rg?b?a?)([0-9]*)([a-z]*)(-srgb)?(-webgl|-unsized)?$/;

export type DecodedTextureFormat = {
Expand All @@ -17,6 +23,13 @@ export type DecodedTextureFormat = {
normalized: boolean;
}

/**
* Returns true if a texture format is GPU compressed
*/
export function isTextureFormatCompressed(textureFormat: TextureFormat): boolean {
return COMPRESSED_TEXTURE_FORMAT_PREFIXES.some(prefix => textureFormat.startsWith(prefix));
}

/**
* Decodes a vertex format, returning type, components, byte length and flags (integer, signed, normalized)
*/
Expand Down
2 changes: 1 addition & 1 deletion modules/core/src/utils/load-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export async function loadImage(
url: string,
opts?: {crossOrigin?: string}
): Promise<HTMLImageElement> {
return new Promise((resolve, reject) => {
return await new Promise((resolve, reject) => {
try {
const image = new Image();
image.onload = () => resolve(image);
Expand Down
3 changes: 1 addition & 2 deletions modules/webgpu/src/adapter/webgpu-canvas-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ export class WebGPUCanvasContext extends CanvasContext {
readonly device: WebGPUDevice;
readonly gpuCanvasContext: GPUCanvasContext;
/** Format of returned textures: "bgra8unorm", "rgba8unorm", "rgba16float". */
// @ts-ignore - TODO - fix this
readonly format: TextureFormat = navigator.gpu.getPreferredCanvasFormat();
readonly format: TextureFormat = navigator.gpu.getPreferredCanvasFormat() as TextureFormat;
/** Default stencil format for depth textures */
depthStencilFormat: TextureFormat = 'depth24plus';

Expand Down
Loading