UNPKG

phaser

Version:

A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.

584 lines (433 loc) 19.7 kB
var WebGLBufferWrapper = require('../../../../src/renderer/webgl/wrappers/WebGLBufferWrapper'); // GL enum constants matching WebGL spec var ARRAY_BUFFER = 34962; var ELEMENT_ARRAY_BUFFER = 34963; var DYNAMIC_DRAW = 35048; var STATIC_DRAW = 35044; function makeMockGl () { return { ARRAY_BUFFER: ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER: ELEMENT_ARRAY_BUFFER, createBuffer: vi.fn().mockReturnValue({ id: 'mock-buffer' }), bufferData: vi.fn(), bufferSubData: vi.fn(), deleteBuffer: vi.fn() }; } function makeMockRenderer (gl) { return { gl: gl, glWrapper: { updateBindingsArrayBuffer: vi.fn(), updateBindingsElementArrayBuffer: vi.fn() } }; } function makeWrapper (bytesOrBuffer, bufferType, bufferUsage, renderer) { var dataBuffer = (bytesOrBuffer instanceof ArrayBuffer) ? bytesOrBuffer : new ArrayBuffer(bytesOrBuffer || 16); if (!bufferType) { bufferType = ARRAY_BUFFER; } if (!bufferUsage) { bufferUsage = DYNAMIC_DRAW; } var gl = makeMockGl(); var r = renderer || makeMockRenderer(gl); return new WebGLBufferWrapper(r, dataBuffer, bufferType, bufferUsage); } describe('WebGLBufferWrapper', function () { describe('constructor', function () { it('should store the renderer reference', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var wrapper = new WebGLBufferWrapper(renderer, new ArrayBuffer(16), ARRAY_BUFFER, DYNAMIC_DRAW); expect(wrapper.renderer).toBe(renderer); }); it('should store the dataBuffer reference', function () { var buf = new ArrayBuffer(16); var wrapper = makeWrapper(buf); expect(wrapper.dataBuffer).toBe(buf); }); it('should store bufferType', function () { var wrapper = makeWrapper(16, ELEMENT_ARRAY_BUFFER, DYNAMIC_DRAW); expect(wrapper.bufferType).toBe(ELEMENT_ARRAY_BUFFER); }); it('should store bufferUsage', function () { var wrapper = makeWrapper(16, ARRAY_BUFFER, STATIC_DRAW); expect(wrapper.bufferUsage).toBe(STATIC_DRAW); }); it('should set webGLBuffer from createResource', function () { var wrapper = makeWrapper(16); expect(wrapper.webGLBuffer).not.toBeNull(); }); it('should create viewU8 as a Uint8Array over the dataBuffer', function () { var buf = new ArrayBuffer(16); var wrapper = makeWrapper(buf); expect(wrapper.viewU8).toBeInstanceOf(Uint8Array); expect(wrapper.viewU8.buffer).toBe(buf); expect(wrapper.viewU8.byteLength).toBe(16); }); it('should create viewF32 when byteLength is divisible by 4', function () { var wrapper = makeWrapper(new ArrayBuffer(16)); expect(wrapper.viewF32).toBeInstanceOf(Float32Array); expect(wrapper.viewF32.length).toBe(4); }); it('should set viewF32 to null when byteLength is not divisible by 4', function () { var wrapper = makeWrapper(new ArrayBuffer(6)); expect(wrapper.viewF32).toBeNull(); }); it('should create viewU16 when byteLength is divisible by 2', function () { var wrapper = makeWrapper(new ArrayBuffer(6)); expect(wrapper.viewU16).toBeInstanceOf(Uint16Array); expect(wrapper.viewU16.length).toBe(3); }); it('should set viewU16 to null when byteLength is odd', function () { var wrapper = makeWrapper(new ArrayBuffer(3)); expect(wrapper.viewU16).toBeNull(); }); it('should create viewU32 when byteLength is divisible by 4', function () { var wrapper = makeWrapper(new ArrayBuffer(8)); expect(wrapper.viewU32).toBeInstanceOf(Uint32Array); expect(wrapper.viewU32.length).toBe(2); }); it('should set viewU32 to null when byteLength is not divisible by 4', function () { var wrapper = makeWrapper(new ArrayBuffer(6)); expect(wrapper.viewU32).toBeNull(); }); it('should call gl.createBuffer during construction', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); new WebGLBufferWrapper(renderer, new ArrayBuffer(16), ARRAY_BUFFER, DYNAMIC_DRAW); expect(gl.createBuffer).toHaveBeenCalledOnce(); }); it('should call gl.bufferData during construction', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var buf = new ArrayBuffer(16); new WebGLBufferWrapper(renderer, buf, ARRAY_BUFFER, DYNAMIC_DRAW); expect(gl.bufferData).toHaveBeenCalledWith(ARRAY_BUFFER, buf, DYNAMIC_DRAW); }); }); describe('createResource', function () { it('should replace webGLBuffer with a new buffer from gl.createBuffer', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var wrapper = new WebGLBufferWrapper(renderer, new ArrayBuffer(16), ARRAY_BUFFER, DYNAMIC_DRAW); var firstBuffer = { id: 'second-buffer' }; gl.createBuffer.mockReturnValueOnce(firstBuffer); wrapper.createResource(); expect(wrapper.webGLBuffer).toBe(firstBuffer); }); it('should call gl.bufferData with current dataBuffer and bufferUsage', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var buf = new ArrayBuffer(16); var wrapper = new WebGLBufferWrapper(renderer, buf, ARRAY_BUFFER, STATIC_DRAW); gl.bufferData.mockClear(); wrapper.createResource(); expect(gl.bufferData).toHaveBeenCalledWith(ARRAY_BUFFER, buf, STATIC_DRAW); }); it('should bind and then unbind the buffer during createResource', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var wrapper = new WebGLBufferWrapper(renderer, new ArrayBuffer(16), ARRAY_BUFFER, DYNAMIC_DRAW); renderer.glWrapper.updateBindingsArrayBuffer.mockClear(); wrapper.createResource(); // bind called with this wrapper, then unbind called with null var calls = renderer.glWrapper.updateBindingsArrayBuffer.mock.calls; expect(calls.length).toBe(2); expect(calls[0][0].bindings.arrayBuffer).toBe(wrapper); expect(calls[1][0].bindings.arrayBuffer).toBeNull(); }); }); describe('bind', function () { it('should call updateBindingsArrayBuffer when bufferType is ARRAY_BUFFER', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var wrapper = new WebGLBufferWrapper(renderer, new ArrayBuffer(16), ARRAY_BUFFER, DYNAMIC_DRAW); renderer.glWrapper.updateBindingsArrayBuffer.mockClear(); wrapper.bind(); expect(renderer.glWrapper.updateBindingsArrayBuffer).toHaveBeenCalledOnce(); expect(renderer.glWrapper.updateBindingsElementArrayBuffer).not.toHaveBeenCalled(); }); it('should pass the wrapper itself when binding', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var wrapper = new WebGLBufferWrapper(renderer, new ArrayBuffer(16), ARRAY_BUFFER, DYNAMIC_DRAW); renderer.glWrapper.updateBindingsArrayBuffer.mockClear(); wrapper.bind(); var call = renderer.glWrapper.updateBindingsArrayBuffer.mock.calls[0]; expect(call[0].bindings.arrayBuffer).toBe(wrapper); }); it('should pass null when unbinding (unbind=true)', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var wrapper = new WebGLBufferWrapper(renderer, new ArrayBuffer(16), ARRAY_BUFFER, DYNAMIC_DRAW); renderer.glWrapper.updateBindingsArrayBuffer.mockClear(); wrapper.bind(true); var call = renderer.glWrapper.updateBindingsArrayBuffer.mock.calls[0]; expect(call[0].bindings.arrayBuffer).toBeNull(); }); it('should call updateBindingsElementArrayBuffer when bufferType is ELEMENT_ARRAY_BUFFER', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var wrapper = new WebGLBufferWrapper(renderer, new ArrayBuffer(16), ELEMENT_ARRAY_BUFFER, DYNAMIC_DRAW); renderer.glWrapper.updateBindingsElementArrayBuffer.mockClear(); wrapper.bind(); expect(renderer.glWrapper.updateBindingsElementArrayBuffer).toHaveBeenCalledOnce(); expect(renderer.glWrapper.updateBindingsArrayBuffer).not.toHaveBeenCalled(); }); it('should pass the wrapper itself for ELEMENT_ARRAY_BUFFER when binding', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var wrapper = new WebGLBufferWrapper(renderer, new ArrayBuffer(16), ELEMENT_ARRAY_BUFFER, DYNAMIC_DRAW); renderer.glWrapper.updateBindingsElementArrayBuffer.mockClear(); wrapper.bind(); var call = renderer.glWrapper.updateBindingsElementArrayBuffer.mock.calls[0]; expect(call[0].bindings.elementArrayBuffer).toBe(wrapper); }); it('should pass null for ELEMENT_ARRAY_BUFFER when unbinding', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var wrapper = new WebGLBufferWrapper(renderer, new ArrayBuffer(16), ELEMENT_ARRAY_BUFFER, DYNAMIC_DRAW); renderer.glWrapper.updateBindingsElementArrayBuffer.mockClear(); wrapper.bind(true); var call = renderer.glWrapper.updateBindingsElementArrayBuffer.mock.calls[0]; expect(call[0].bindings.elementArrayBuffer).toBeNull(); }); it('should not call either update method for an unknown bufferType', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var wrapper = new WebGLBufferWrapper(renderer, new ArrayBuffer(16), 9999, DYNAMIC_DRAW); renderer.glWrapper.updateBindingsArrayBuffer.mockClear(); renderer.glWrapper.updateBindingsElementArrayBuffer.mockClear(); wrapper.bind(); expect(renderer.glWrapper.updateBindingsArrayBuffer).not.toHaveBeenCalled(); expect(renderer.glWrapper.updateBindingsElementArrayBuffer).not.toHaveBeenCalled(); }); }); describe('update', function () { it('should call bind before uploading data', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var wrapper = new WebGLBufferWrapper(renderer, new ArrayBuffer(16), ARRAY_BUFFER, DYNAMIC_DRAW); renderer.glWrapper.updateBindingsArrayBuffer.mockClear(); wrapper.update(); expect(renderer.glWrapper.updateBindingsArrayBuffer).toHaveBeenCalled(); }); it('should call gl.bufferSubData with the entire dataBuffer when bytes is undefined', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var buf = new ArrayBuffer(16); var wrapper = new WebGLBufferWrapper(renderer, buf, ARRAY_BUFFER, DYNAMIC_DRAW); wrapper.update(); var lastCall = gl.bufferSubData.mock.calls[gl.bufferSubData.mock.calls.length - 1]; expect(lastCall[0]).toBe(ARRAY_BUFFER); expect(lastCall[1]).toBe(0); expect(lastCall[2]).toBe(buf); }); it('should use offset=0 by default when offset is undefined', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var wrapper = new WebGLBufferWrapper(renderer, new ArrayBuffer(16), ARRAY_BUFFER, DYNAMIC_DRAW); wrapper.update(8); var lastCall = gl.bufferSubData.mock.calls[gl.bufferSubData.mock.calls.length - 1]; expect(lastCall[1]).toBe(0); }); it('should call gl.bufferSubData with a subarray when bytes is specified', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var wrapper = new WebGLBufferWrapper(renderer, new ArrayBuffer(16), ARRAY_BUFFER, DYNAMIC_DRAW); wrapper.update(8, 0); var lastCall = gl.bufferSubData.mock.calls[gl.bufferSubData.mock.calls.length - 1]; expect(lastCall[0]).toBe(ARRAY_BUFFER); expect(lastCall[2]).toBeInstanceOf(Uint8Array); expect(lastCall[2].byteLength).toBe(8); }); it('should pass the given offset to gl.bufferSubData', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var wrapper = new WebGLBufferWrapper(renderer, new ArrayBuffer(16), ARRAY_BUFFER, DYNAMIC_DRAW); wrapper.update(4, 4); var lastCall = gl.bufferSubData.mock.calls[gl.bufferSubData.mock.calls.length - 1]; expect(lastCall[1]).toBe(4); }); it('should upload a subarray starting at offset when bytes and offset are specified', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var wrapper = new WebGLBufferWrapper(renderer, new ArrayBuffer(16), ARRAY_BUFFER, DYNAMIC_DRAW); wrapper.update(4, 8); var lastCall = gl.bufferSubData.mock.calls[gl.bufferSubData.mock.calls.length - 1]; var subarray = lastCall[2]; expect(subarray).toBeInstanceOf(Uint8Array); expect(subarray.byteLength).toBe(4); }); }); describe('resize', function () { it('should replace dataBuffer with a new ArrayBuffer of the specified size', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var wrapper = new WebGLBufferWrapper(renderer, new ArrayBuffer(16), ARRAY_BUFFER, DYNAMIC_DRAW); var oldBuffer = wrapper.dataBuffer; wrapper.resize(32); expect(wrapper.dataBuffer).not.toBe(oldBuffer); expect(wrapper.dataBuffer.byteLength).toBe(32); }); it('should recreate viewU8 to reflect the new size', function () { var wrapper = makeWrapper(16); wrapper.resize(32); expect(wrapper.viewU8).toBeInstanceOf(Uint8Array); expect(wrapper.viewU8.byteLength).toBe(32); }); it('should recreate viewF32 when new size is divisible by 4', function () { var wrapper = makeWrapper(16); wrapper.resize(20); expect(wrapper.viewF32).toBeInstanceOf(Float32Array); expect(wrapper.viewF32.length).toBe(5); }); it('should set viewF32 to null when new size is not divisible by 4', function () { var wrapper = makeWrapper(16); wrapper.resize(6); expect(wrapper.viewF32).toBeNull(); }); it('should recreate viewU16 when new size is divisible by 2', function () { var wrapper = makeWrapper(16); wrapper.resize(6); expect(wrapper.viewU16).toBeInstanceOf(Uint16Array); expect(wrapper.viewU16.length).toBe(3); }); it('should set viewU16 to null when new size is odd', function () { var wrapper = makeWrapper(16); wrapper.resize(3); expect(wrapper.viewU16).toBeNull(); }); it('should recreate viewU32 when new size is divisible by 4', function () { var wrapper = makeWrapper(16); wrapper.resize(8); expect(wrapper.viewU32).toBeInstanceOf(Uint32Array); expect(wrapper.viewU32.length).toBe(2); }); it('should set viewU32 to null when new size is not divisible by 4', function () { var wrapper = makeWrapper(16); wrapper.resize(6); expect(wrapper.viewU32).toBeNull(); }); it('should call gl.bufferData with the new dataBuffer after resize', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var wrapper = new WebGLBufferWrapper(renderer, new ArrayBuffer(16), ARRAY_BUFFER, DYNAMIC_DRAW); gl.bufferData.mockClear(); wrapper.resize(32); expect(gl.bufferData).toHaveBeenCalledWith(ARRAY_BUFFER, wrapper.dataBuffer, DYNAMIC_DRAW); }); it('should bind the buffer during resize', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var wrapper = new WebGLBufferWrapper(renderer, new ArrayBuffer(16), ARRAY_BUFFER, DYNAMIC_DRAW); renderer.glWrapper.updateBindingsArrayBuffer.mockClear(); wrapper.resize(32); expect(renderer.glWrapper.updateBindingsArrayBuffer).toHaveBeenCalled(); }); }); describe('destroy', function () { it('should call gl.deleteBuffer with the current webGLBuffer', function () { var gl = makeMockGl(); var renderer = makeMockRenderer(gl); var wrapper = new WebGLBufferWrapper(renderer, new ArrayBuffer(16), ARRAY_BUFFER, DYNAMIC_DRAW); var buffer = wrapper.webGLBuffer; wrapper.destroy(); expect(gl.deleteBuffer).toHaveBeenCalledWith(buffer); }); it('should set webGLBuffer to null', function () { var wrapper = makeWrapper(16); wrapper.destroy(); expect(wrapper.webGLBuffer).toBeNull(); }); it('should set dataBuffer to null', function () { var wrapper = makeWrapper(16); wrapper.destroy(); expect(wrapper.dataBuffer).toBeNull(); }); it('should set viewF32 to null', function () { var wrapper = makeWrapper(16); wrapper.destroy(); expect(wrapper.viewF32).toBeNull(); }); it('should set viewU8 to null', function () { var wrapper = makeWrapper(16); wrapper.destroy(); expect(wrapper.viewU8).toBeNull(); }); it('should set viewU16 to null', function () { var wrapper = makeWrapper(16); wrapper.destroy(); expect(wrapper.viewU16).toBeNull(); }); it('should set viewU32 to null', function () { var wrapper = makeWrapper(16); wrapper.destroy(); expect(wrapper.viewU32).toBeNull(); }); it('should set renderer to null', function () { var wrapper = makeWrapper(16); wrapper.destroy(); expect(wrapper.renderer).toBeNull(); }); }); });