phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
576 lines (463 loc) • 21.2 kB
JavaScript
var WebGLFramebufferWrapper = require('../../../../src/renderer/webgl/wrappers/WebGLFramebufferWrapper');
function createMockGL ()
{
var mockFramebuffer = { _type: 'framebuffer' };
var mockRenderbuffer = { _type: 'renderbuffer' };
return {
COLOR_ATTACHMENT0: 36064,
DEPTH_STENCIL_ATTACHMENT: 33306,
DEPTH_ATTACHMENT: 36096,
STENCIL_ATTACHMENT: 36128,
DEPTH_STENCIL: 34041,
DEPTH_COMPONENT16: 33189,
STENCIL_INDEX8: 36168,
FRAMEBUFFER: 36160,
FRAMEBUFFER_COMPLETE: 36053,
RENDERBUFFER: 36161,
TEXTURE_2D: 3553,
createFramebuffer: vi.fn(function () { return mockFramebuffer; }),
deleteFramebuffer: vi.fn(),
createRenderbuffer: vi.fn(function () { return mockRenderbuffer; }),
deleteRenderbuffer: vi.fn(),
framebufferTexture2D: vi.fn(),
checkFramebufferStatus: vi.fn(function () { return 36053; }),
renderbufferStorage: vi.fn(),
framebufferRenderbuffer: vi.fn()
};
}
function createMockGLWrapper ()
{
return {
updateBindingsFramebuffer: vi.fn(),
updateBindingsRenderbuffer: vi.fn()
};
}
function createMockRenderer (gl, glWrapper)
{
return {
gl: gl || createMockGL(),
glWrapper: glWrapper || createMockGLWrapper(),
deleteTexture: vi.fn()
};
}
function createMockTexture (width, height)
{
return {
width: width !== undefined ? width : 256,
height: height !== undefined ? height : 256,
webGLTexture: { _type: 'texture' },
isRenderTexture: false,
resize: vi.fn()
};
}
describe('WebGLFramebufferWrapper', function ()
{
describe('constructor', function ()
{
it('should set useCanvas to true when colorAttachments is null', function ()
{
var renderer = createMockRenderer();
var wrapper = new WebGLFramebufferWrapper(renderer, null);
expect(wrapper.useCanvas).toBe(true);
});
it('should set useCanvas to true when colorAttachments is empty array', function ()
{
var renderer = createMockRenderer();
var wrapper = new WebGLFramebufferWrapper(renderer, []);
expect(wrapper.useCanvas).toBe(true);
});
it('should set useCanvas to false when colorAttachments has entries', function ()
{
var renderer = createMockRenderer();
var texture = createMockTexture();
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ]);
expect(wrapper.useCanvas).toBe(false);
});
it('should store a reference to the renderer', function ()
{
var renderer = createMockRenderer();
var wrapper = new WebGLFramebufferWrapper(renderer, null);
expect(wrapper.renderer).toBe(renderer);
});
it('should have null webGLFramebuffer when using canvas', function ()
{
var renderer = createMockRenderer();
var wrapper = new WebGLFramebufferWrapper(renderer, null);
expect(wrapper.webGLFramebuffer).toBeNull();
});
it('should have null renderTexture when using canvas', function ()
{
var renderer = createMockRenderer();
var wrapper = new WebGLFramebufferWrapper(renderer, null);
expect(wrapper.renderTexture).toBeNull();
});
it('should have zero width and height when using canvas', function ()
{
var renderer = createMockRenderer();
var wrapper = new WebGLFramebufferWrapper(renderer, null);
expect(wrapper.width).toBe(0);
expect(wrapper.height).toBe(0);
});
it('should have empty attachments array when using canvas', function ()
{
var renderer = createMockRenderer();
var wrapper = new WebGLFramebufferWrapper(renderer, null);
expect(wrapper.attachments.length).toBe(0);
});
it('should set width and height from first color attachment', function ()
{
var renderer = createMockRenderer();
var texture = createMockTexture(512, 256);
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ]);
expect(wrapper.width).toBe(512);
expect(wrapper.height).toBe(256);
});
it('should set renderTexture from the first color attachment', function ()
{
var renderer = createMockRenderer();
var texture = createMockTexture();
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ]);
expect(wrapper.renderTexture).toBe(texture);
});
it('should create an attachment entry for each color attachment', function ()
{
var renderer = createMockRenderer();
var texture1 = createMockTexture(128, 128);
var texture2 = createMockTexture(128, 128);
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture1, texture2 ]);
expect(wrapper.attachments[0].texture).toBe(texture1);
expect(wrapper.attachments[0].attachmentPoint).toBe(renderer.gl.COLOR_ATTACHMENT0);
expect(wrapper.attachments[1].texture).toBe(texture2);
expect(wrapper.attachments[1].attachmentPoint).toBe(renderer.gl.COLOR_ATTACHMENT0 + 1);
});
it('should throw when color attachments have different dimensions', function ()
{
var renderer = createMockRenderer();
var texture1 = createMockTexture(256, 256);
var texture2 = createMockTexture(128, 128);
expect(function ()
{
new WebGLFramebufferWrapper(renderer, [ texture1, texture2 ]);
}).toThrow('Color attachments must have the same dimensions');
});
it('should add a DEPTH_STENCIL attachment when both depth and stencil are requested', function ()
{
var gl = createMockGL();
var renderer = createMockRenderer(gl, createMockGLWrapper());
var texture = createMockTexture();
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ], true, true);
var lastAttachment = wrapper.attachments[wrapper.attachments.length - 1];
expect(lastAttachment.attachmentPoint).toBe(gl.DEPTH_STENCIL_ATTACHMENT);
expect(lastAttachment.internalFormat).toBe(gl.DEPTH_STENCIL);
});
it('should add a DEPTH attachment when only depth is requested', function ()
{
var gl = createMockGL();
var renderer = createMockRenderer(gl, createMockGLWrapper());
var texture = createMockTexture();
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ], false, true);
var lastAttachment = wrapper.attachments[wrapper.attachments.length - 1];
expect(lastAttachment.attachmentPoint).toBe(gl.DEPTH_ATTACHMENT);
expect(lastAttachment.internalFormat).toBe(gl.DEPTH_COMPONENT16);
});
it('should add a STENCIL attachment when only stencil is requested', function ()
{
var gl = createMockGL();
var renderer = createMockRenderer(gl, createMockGLWrapper());
var texture = createMockTexture();
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ], true, false);
var lastAttachment = wrapper.attachments[wrapper.attachments.length - 1];
expect(lastAttachment.attachmentPoint).toBe(gl.STENCIL_ATTACHMENT);
expect(lastAttachment.internalFormat).toBe(gl.STENCIL_INDEX8);
});
it('should not add depth or stencil attachments when neither is requested', function ()
{
var renderer = createMockRenderer();
var texture = createMockTexture();
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ], false, false);
expect(wrapper.attachments.length).toBe(1);
});
it('should not add depth or stencil attachments when using canvas', function ()
{
var renderer = createMockRenderer();
var wrapper = new WebGLFramebufferWrapper(renderer, null, true, true);
expect(wrapper.attachments.length).toBe(0);
});
});
describe('createResource', function ()
{
it('should return early without modifying webGLFramebuffer when useCanvas is true', function ()
{
var gl = createMockGL();
var renderer = createMockRenderer(gl, createMockGLWrapper());
var wrapper = new WebGLFramebufferWrapper(renderer, null);
wrapper.createResource();
expect(wrapper.webGLFramebuffer).toBeNull();
expect(gl.createFramebuffer).not.toHaveBeenCalled();
});
it('should create a WebGL framebuffer and assign it', function ()
{
var gl = createMockGL();
var glWrapper = createMockGLWrapper();
var renderer = createMockRenderer(gl, glWrapper);
var texture = createMockTexture();
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ]);
expect(gl.createFramebuffer).toHaveBeenCalled();
expect(wrapper.webGLFramebuffer).not.toBeNull();
});
it('should call updateBindingsFramebuffer after creating the framebuffer', function ()
{
var gl = createMockGL();
var glWrapper = createMockGLWrapper();
var renderer = createMockRenderer(gl, glWrapper);
var texture = createMockTexture();
new WebGLFramebufferWrapper(renderer, [ texture ]);
expect(glWrapper.updateBindingsFramebuffer).toHaveBeenCalled();
});
it('should mark texture as isRenderTexture when attaching', function ()
{
var renderer = createMockRenderer();
var texture = createMockTexture();
new WebGLFramebufferWrapper(renderer, [ texture ]);
expect(texture.isRenderTexture).toBe(true);
});
it('should call framebufferTexture2D for each texture attachment', function ()
{
var gl = createMockGL();
var renderer = createMockRenderer(gl, createMockGLWrapper());
var texture = createMockTexture();
new WebGLFramebufferWrapper(renderer, [ texture ]);
expect(gl.framebufferTexture2D).toHaveBeenCalledWith(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
texture.webGLTexture,
0
);
});
it('should delete the existing framebuffer if one already exists when called again', function ()
{
var gl = createMockGL();
var renderer = createMockRenderer(gl, createMockGLWrapper());
var texture = createMockTexture();
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ]);
var firstFramebuffer = wrapper.webGLFramebuffer;
gl.deleteFramebuffer.mockClear();
wrapper.createResource();
expect(gl.deleteFramebuffer).toHaveBeenCalledWith(firstFramebuffer);
});
it('should create a renderbuffer for non-texture attachments', function ()
{
var gl = createMockGL();
var glWrapper = createMockGLWrapper();
var renderer = createMockRenderer(gl, glWrapper);
var texture = createMockTexture();
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ], false, true);
var depthAttachment = wrapper.attachments[1];
expect(depthAttachment.renderbuffer).not.toBeUndefined();
expect(gl.createRenderbuffer).toHaveBeenCalled();
expect(gl.renderbufferStorage).toHaveBeenCalled();
expect(gl.framebufferRenderbuffer).toHaveBeenCalled();
});
it('should throw when framebuffer status is incomplete before attaching renderbuffer', function ()
{
var gl = createMockGL();
gl.checkFramebufferStatus = vi.fn(function () { return 36054; }); // Incomplete Attachment
var renderer = createMockRenderer(gl, createMockGLWrapper());
var texture = createMockTexture();
expect(function ()
{
new WebGLFramebufferWrapper(renderer, [ texture ], false, true);
}).toThrow('Framebuffer status: Incomplete Attachment');
});
it('should throw with the raw status code when the error code is unknown', function ()
{
var gl = createMockGL();
gl.checkFramebufferStatus = vi.fn(function () { return 99999; });
var renderer = createMockRenderer(gl, createMockGLWrapper());
var texture = createMockTexture();
expect(function ()
{
new WebGLFramebufferWrapper(renderer, [ texture ], false, true);
}).toThrow('Framebuffer status: 99999');
});
it('should delete existing renderbuffers when recreating', function ()
{
var gl = createMockGL();
var glWrapper = createMockGLWrapper();
var renderer = createMockRenderer(gl, glWrapper);
var texture = createMockTexture();
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ], false, true);
var oldRenderbuffer = wrapper.attachments[1].renderbuffer;
gl.deleteRenderbuffer.mockClear();
wrapper.createResource();
expect(gl.deleteRenderbuffer).toHaveBeenCalledWith(oldRenderbuffer);
});
});
describe('resize', function ()
{
it('should return early without changing dimensions when useCanvas is true', function ()
{
var renderer = createMockRenderer();
var wrapper = new WebGLFramebufferWrapper(renderer, null);
wrapper.resize(800, 600);
expect(wrapper.width).toBe(0);
expect(wrapper.height).toBe(0);
});
it('should update width and height', function ()
{
var renderer = createMockRenderer();
var texture = createMockTexture(256, 256);
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ]);
wrapper.resize(800, 600);
expect(wrapper.width).toBe(800);
expect(wrapper.height).toBe(600);
});
it('should call resize on the renderTexture', function ()
{
var renderer = createMockRenderer();
var texture = createMockTexture(256, 256);
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ]);
wrapper.resize(800, 600);
expect(texture.resize).toHaveBeenCalledWith(800, 600);
});
it('should call createResource after resizing', function ()
{
var gl = createMockGL();
var renderer = createMockRenderer(gl, createMockGLWrapper());
var texture = createMockTexture(256, 256);
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ]);
var callCount = gl.createFramebuffer.mock.calls.length;
wrapper.resize(128, 128);
expect(gl.createFramebuffer.mock.calls.length).toBeGreaterThan(callCount);
});
it('should not call renderTexture.resize when useCanvas is true', function ()
{
var renderer = createMockRenderer();
var wrapper = new WebGLFramebufferWrapper(renderer, null);
// renderTexture is null for canvas mode; resize returns early,
// so no attempt to call a method on null should occur
expect(function ()
{
wrapper.resize(800, 600);
}).not.toThrow();
});
});
describe('destroy', function ()
{
it('should return early without throwing when renderer is already null', function ()
{
var renderer = createMockRenderer();
var wrapper = new WebGLFramebufferWrapper(renderer, null);
wrapper.renderer = null;
expect(function ()
{
wrapper.destroy();
}).not.toThrow();
});
it('should set renderer to null', function ()
{
var renderer = createMockRenderer();
var wrapper = new WebGLFramebufferWrapper(renderer, null);
wrapper.destroy();
expect(wrapper.renderer).toBeNull();
});
it('should set webGLFramebuffer to null', function ()
{
var renderer = createMockRenderer();
var texture = createMockTexture();
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ]);
wrapper.destroy();
expect(wrapper.webGLFramebuffer).toBeNull();
});
it('should set renderTexture to null', function ()
{
var renderer = createMockRenderer();
var texture = createMockTexture();
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ]);
wrapper.destroy();
expect(wrapper.renderTexture).toBeNull();
});
it('should empty the attachments array', function ()
{
var renderer = createMockRenderer();
var texture = createMockTexture();
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ]);
wrapper.destroy();
expect(wrapper.attachments.length).toBe(0);
});
it('should call deleteFramebuffer on the GL context', function ()
{
var gl = createMockGL();
var renderer = createMockRenderer(gl, createMockGLWrapper());
var texture = createMockTexture();
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ]);
var fb = wrapper.webGLFramebuffer;
wrapper.destroy();
expect(gl.deleteFramebuffer).toHaveBeenCalledWith(fb);
});
it('should call deleteTexture for texture attachments', function ()
{
var gl = createMockGL();
var renderer = createMockRenderer(gl, createMockGLWrapper());
var texture = createMockTexture();
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ]);
wrapper.destroy();
expect(renderer.deleteTexture).toHaveBeenCalledWith(texture);
});
it('should call framebufferTexture2D with null when detaching texture', function ()
{
var gl = createMockGL();
var renderer = createMockRenderer(gl, createMockGLWrapper());
var texture = createMockTexture();
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ]);
wrapper.destroy();
expect(gl.framebufferTexture2D).toHaveBeenCalledWith(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
null,
0
);
});
it('should call deleteRenderbuffer for renderbuffer attachments', function ()
{
var gl = createMockGL();
var glWrapper = createMockGLWrapper();
var renderer = createMockRenderer(gl, glWrapper);
var texture = createMockTexture();
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ], false, true);
var renderbuffer = wrapper.attachments[1].renderbuffer;
gl.deleteRenderbuffer.mockClear();
wrapper.destroy();
expect(gl.deleteRenderbuffer).toHaveBeenCalledWith(renderbuffer);
});
it('should call updateBindingsFramebuffer to clear global bindings', function ()
{
var gl = createMockGL();
var glWrapper = createMockGLWrapper();
var renderer = createMockRenderer(gl, glWrapper);
var texture = createMockTexture();
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ]);
glWrapper.updateBindingsFramebuffer.mockClear();
wrapper.destroy();
var calls = glWrapper.updateBindingsFramebuffer.mock.calls;
var lastCall = calls[calls.length - 1];
expect(lastCall[0].bindings.framebuffer).toBeNull();
});
it('should be idempotent - second destroy is a no-op', function ()
{
var gl = createMockGL();
var renderer = createMockRenderer(gl, createMockGLWrapper());
var texture = createMockTexture();
var wrapper = new WebGLFramebufferWrapper(renderer, [ texture ]);
wrapper.destroy();
gl.deleteFramebuffer.mockClear();
expect(function ()
{
wrapper.destroy();
}).not.toThrow();
expect(gl.deleteFramebuffer).not.toHaveBeenCalled();
});
});
});