You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1034 lines
29 KiB
JavaScript

import { Color } from "../../Source/Cesium.js";
import { PixelFormat } from "../../Source/Cesium.js";
import { PrimitiveType } from "../../Source/Cesium.js";
import { WebGLConstants } from "../../Source/Cesium.js";
import { Buffer } from "../../Source/Cesium.js";
import { BufferUsage } from "../../Source/Cesium.js";
import { ClearCommand } from "../../Source/Cesium.js";
import { ContextLimits } from "../../Source/Cesium.js";
import { CubeMap } from "../../Source/Cesium.js";
import { DrawCommand } from "../../Source/Cesium.js";
import { Framebuffer } from "../../Source/Cesium.js";
import { PixelDatatype } from "../../Source/Cesium.js";
import { Renderbuffer } from "../../Source/Cesium.js";
import { RenderbufferFormat } from "../../Source/Cesium.js";
import { RenderState } from "../../Source/Cesium.js";
import { ShaderProgram } from "../../Source/Cesium.js";
import { Texture } from "../../Source/Cesium.js";
import { VertexArray } from "../../Source/Cesium.js";
import createContext from "../createContext.js";
describe(
"Renderer/Framebuffer",
function () {
var context;
var sp;
var va;
var framebuffer;
beforeAll(function () {
context = createContext();
});
afterAll(function () {
context.destroyForSpecs();
});
afterEach(function () {
sp = sp && sp.destroy();
va = va && va.destroy();
framebuffer = framebuffer && framebuffer.destroy();
});
it("has a color texture attachment", function () {
framebuffer = new Framebuffer({
context: context,
colorTextures: [
new Texture({
context: context,
width: 1,
height: 1,
}),
],
});
expect(framebuffer.getColorTexture(0)).toBeDefined();
});
it("has a color renderbuffer attachment", function () {
framebuffer = new Framebuffer({
context: context,
colorRenderbuffers: [
new Renderbuffer({
context: context,
format: RenderbufferFormat.RGBA4,
}),
],
});
expect(framebuffer.getColorRenderbuffer(0)).toBeDefined();
});
it("has a depth texture attachment", function () {
if (context.depthTexture) {
framebuffer = new Framebuffer({
context: context,
depthTexture: new Texture({
context: context,
width: 1,
height: 1,
pixelFormat: PixelFormat.DEPTH_COMPONENT,
pixelDatatype: PixelDatatype.UNSIGNED_SHORT,
}),
});
expect(framebuffer.depthTexture).toBeDefined();
}
});
it("has a depth renderbuffer attachment", function () {
framebuffer = new Framebuffer({
context: context,
depthRenderbuffer: new Renderbuffer({
context: context,
format: RenderbufferFormat.DEPTH_COMPONENT16,
}),
});
expect(framebuffer.depthRenderbuffer).toBeDefined();
});
it("has a stencil renderbuffer attachment", function () {
framebuffer = new Framebuffer({
context: context,
stencilRenderbuffer: new Renderbuffer({
context: context,
format: RenderbufferFormat.STENCIL_INDEX8,
}),
});
expect(framebuffer.stencilRenderbuffer).toBeDefined();
});
it("has a depth-stencil texture attachment", function () {
if (context.depthTexture) {
framebuffer = new Framebuffer({
context: context,
depthStencilTexture: new Texture({
context: context,
width: 1,
height: 1,
pixelFormat: PixelFormat.DEPTH_STENCIL,
pixelDatatype: PixelDatatype.UNSIGNED_INT_24_8,
}),
});
expect(framebuffer.depthStencilTexture).toBeDefined();
}
});
it("has a depth-stencil renderbuffer attachment", function () {
framebuffer = new Framebuffer({
context: context,
depthStencilRenderbuffer: new Renderbuffer({
context: context,
format: RenderbufferFormat.DEPTH_STENCIL,
}),
});
expect(framebuffer.depthStencilRenderbuffer).toBeDefined();
});
it("has a depth attachment", function () {
framebuffer = new Framebuffer({
context: context,
});
expect(framebuffer.hasDepthAttachment).toEqual(false);
framebuffer.destroy();
framebuffer = new Framebuffer({
context: context,
depthRenderbuffer: new Renderbuffer({
context: context,
format: RenderbufferFormat.DEPTH_COMPONENT16,
}),
});
expect(framebuffer.hasDepthAttachment).toEqual(true);
});
it("clears a color attachment", function () {
// 1 of 3. Clear default color buffer to black.
ClearCommand.ALL.execute(context);
expect(context).toReadPixels([0, 0, 0, 255]);
// 2 of 3. Clear framebuffer color attachment to green.
var colorTexture = new Texture({
context: context,
width: 1,
height: 1,
});
framebuffer = new Framebuffer({
context: context,
colorTextures: [colorTexture],
});
var clearCommand = new ClearCommand({
color: new Color(0.0, 1.0, 0.0, 1.0),
framebuffer: framebuffer,
});
clearCommand.execute(context);
// 3 of 3. Render green to default color buffer by reading from previous color attachment
var fs =
"uniform sampler2D u_texture; void main() { gl_FragColor = texture2D(u_texture, vec2(0.0)); }";
var uniformMap = {
u_texture: function () {
return colorTexture;
},
};
expect({
context: context,
fragmentShader: fs,
uniformMap: uniformMap,
}).contextToRender([0, 255, 0, 255]);
});
it("clears a cube map face color attachment", function () {
var cubeMap = new CubeMap({
context: context,
width: 1,
height: 1,
});
// 1 of 3. Clear default color buffer to black.
ClearCommand.ALL.execute(context);
expect(context).toReadPixels([0, 0, 0, 255]);
// 2 of 3. Clear framebuffer color attachment to green.
framebuffer = new Framebuffer({
context: context,
colorTextures: [cubeMap.positiveX],
destroyAttachments: false,
});
var clearCommand = new ClearCommand({
color: new Color(0.0, 1.0, 0.0, 1.0),
framebuffer: framebuffer,
});
clearCommand.execute(context);
// 3 of 3. Render green to default color buffer by reading from previous color attachment
var fs =
"uniform samplerCube u_cubeMap; void main() { gl_FragColor = textureCube(u_cubeMap, vec3(1.0, 0.0, 0.0)); }";
var uniformMap = {
u_cubeMap: function () {
return cubeMap;
},
};
expect({
context: context,
fragmentShader: fs,
uniformMap: uniformMap,
}).contextToRender([0, 255, 0, 255]);
cubeMap = cubeMap.destroy();
});
function renderColorTexture(framebuffer, color, expectedColor) {
var vs =
"attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }";
var fs = "uniform vec4 color; void main() { gl_FragColor = color; }";
sp = ShaderProgram.fromCache({
context: context,
vertexShaderSource: vs,
fragmentShaderSource: fs,
attributeLocations: {
position: 0,
},
});
va = new VertexArray({
context: context,
attributes: [
{
index: 0,
vertexBuffer: Buffer.createVertexBuffer({
context: context,
typedArray: new Float32Array([0, 0, 0, 1]),
usage: BufferUsage.STATIC_DRAW,
}),
componentsPerAttribute: 4,
},
],
});
var uniformMap = {
color: function () {
return color;
},
};
var command = new DrawCommand({
primitiveType: PrimitiveType.POINTS,
shaderProgram: sp,
vertexArray: va,
uniformMap: uniformMap,
framebuffer: framebuffer,
});
command.execute(context);
expect({
context: context,
framebuffer: framebuffer,
}).toReadPixels(expectedColor);
}
it("draws to a color attachment", function () {
framebuffer = new Framebuffer({
context: context,
colorTextures: [
new Texture({
context: context,
width: 1,
height: 1,
}),
],
});
renderColorTexture(framebuffer, new Color(0.0, 1.0, 0.0, 1.0), [
0,
255,
0,
255,
]);
});
it("draws to a floating-point color attachment", function () {
if (!context.colorBufferFloat) {
return;
}
framebuffer = new Framebuffer({
context: context,
colorTextures: [
new Texture({
context: context,
width: 1,
height: 1,
pixelDatatype: PixelDatatype.FLOAT,
}),
],
});
renderColorTexture(framebuffer, new Color(0.5, 1.5, 2.0, 1.0), [
0.5,
1.5,
2.0,
1.0,
]);
});
it("draws to a half floating-point color attachment", function () {
if (!context.colorBufferHalfFloat) {
return;
}
framebuffer = new Framebuffer({
context: context,
colorTextures: [
new Texture({
context: context,
width: 1,
height: 1,
pixelDatatype: PixelDatatype.HALF_FLOAT,
}),
],
});
renderColorTexture(framebuffer, new Color(0.5, 1.5, 2.0, 1.0), [
14336,
15872,
16384,
15360,
]);
});
function renderDepthAttachment(framebuffer, texture) {
ClearCommand.ALL.execute(context);
var framebufferClear = new ClearCommand({
depth: 1.0,
framebuffer: framebuffer,
});
framebufferClear.execute(context);
// 1 of 3. Render green point into color attachment.
var vs =
"attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }";
var fs = "void main() { gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); }";
sp = ShaderProgram.fromCache({
context: context,
vertexShaderSource: vs,
fragmentShaderSource: fs,
attributeLocations: {
position: 0,
},
});
va = new VertexArray({
context: context,
attributes: [
{
index: 0,
vertexBuffer: Buffer.createVertexBuffer({
context: context,
typedArray: new Float32Array([0, 0, 0, 1]),
usage: BufferUsage.STATIC_DRAW,
}),
componentsPerAttribute: 4,
},
],
});
var command = new DrawCommand({
primitiveType: PrimitiveType.POINTS,
shaderProgram: sp,
vertexArray: va,
framebuffer: framebuffer,
renderState: RenderState.fromCache({
depthTest: {
enabled: true,
},
}),
});
command.execute(context);
// 2 of 3. Verify default color buffer is still black.
expect(context).toReadPixels([0, 0, 0, 255]);
// 3 of 3. Render green to default color buffer by reading from previous color attachment
var vs2 =
"attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }";
var fs2 =
"uniform sampler2D u_texture; void main() { gl_FragColor = texture2D(u_texture, vec2(0.0)).rrrr; }";
var sp2 = ShaderProgram.fromCache({
context: context,
vertexShaderSource: vs2,
fragmentShaderSource: fs2,
attributeLocations: {
position: 0,
},
});
var uniformMap = {
u_texture: function () {
return texture;
},
};
command = new DrawCommand({
primitiveType: PrimitiveType.POINTS,
shaderProgram: sp2,
vertexArray: va,
uniformMap: uniformMap,
});
command.execute(context);
sp2 = sp2.destroy();
return context.readPixels();
}
it("draws to a depth texture attachment", function () {
if (context.depthTexture) {
framebuffer = new Framebuffer({
context: context,
colorTextures: [
new Texture({
context: context,
width: 1,
height: 1,
}),
],
depthTexture: new Texture({
context: context,
width: 1,
height: 1,
pixelFormat: PixelFormat.DEPTH_COMPONENT,
pixelDatatype: PixelDatatype.UNSIGNED_SHORT,
}),
});
if (framebuffer.status === WebGLConstants.FRAMEBUFFER_COMPLETE) {
expect(
renderDepthAttachment(framebuffer, framebuffer.depthTexture)
).toEqualEpsilon([128, 128, 128, 255], 1);
}
}
});
it("draws to a depth-stencil texture attachment", function () {
if (context.depthTexture) {
framebuffer = new Framebuffer({
context: context,
colorTextures: [
new Texture({
context: context,
width: 1,
height: 1,
}),
],
depthStencilTexture: new Texture({
context: context,
width: 1,
height: 1,
pixelFormat: PixelFormat.DEPTH_STENCIL,
pixelDatatype: PixelDatatype.UNSIGNED_INT_24_8,
}),
});
if (framebuffer.status === WebGLConstants.FRAMEBUFFER_COMPLETE) {
expect(
renderDepthAttachment(framebuffer, framebuffer.depthStencilTexture)
).toEqualEpsilon([128, 128, 128, 255], 1);
}
}
});
it("draws with a depth attachment", function () {
framebuffer = new Framebuffer({
context: context,
colorTextures: [
new Texture({
context: context,
width: 1,
height: 1,
}),
],
depthRenderbuffer: new Renderbuffer({
context: context,
format: RenderbufferFormat.DEPTH_COMPONENT16,
width: 1,
height: 1,
}),
});
var vs =
"attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }";
var fs = "void main() { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); }";
sp = ShaderProgram.fromCache({
context: context,
vertexShaderSource: vs,
fragmentShaderSource: fs,
attributeLocations: {
position: 0,
},
});
va = new VertexArray({
context: context,
attributes: [
{
index: 0,
vertexBuffer: Buffer.createVertexBuffer({
context: context,
typedArray: new Float32Array([0, 0, 0, 1]),
usage: BufferUsage.STATIC_DRAW,
}),
componentsPerAttribute: 4,
},
],
});
// 1 of 3. Clear framebuffer
var clearCommand = new ClearCommand({
color: new Color(0.0, 0.0, 0.0, 0.0),
depth: 1.0,
framebuffer: framebuffer,
});
clearCommand.execute(context);
expect({
context: context,
framebuffer: framebuffer,
}).toReadPixels([0, 0, 0, 0]);
// 2 of 3. Does not pass depth test
var command = new DrawCommand({
primitiveType: PrimitiveType.POINTS,
shaderProgram: sp,
vertexArray: va,
framebuffer: framebuffer,
renderState: RenderState.fromCache({
depthTest: {
enabled: true,
func: WebGLConstants.NEVER,
},
}),
});
command.execute(context);
expect({
context: context,
framebuffer: framebuffer,
}).toReadPixels([0, 0, 0, 0]);
// 3 of 3. Passes depth test
command = new DrawCommand({
primitiveType: PrimitiveType.POINTS,
shaderProgram: sp,
vertexArray: va,
framebuffer: framebuffer,
renderState: RenderState.fromCache({
depthTest: {
enabled: true,
func: WebGLConstants.ALWAYS,
},
}),
});
command.execute(context);
expect({
context: context,
framebuffer: framebuffer,
}).toReadPixels([255, 255, 255, 255]);
});
it("draws with multiple render targets", function () {
if (!context.drawBuffers) {
return;
}
var source = new Uint8Array(4);
var colorTexture0 = new Texture({
context: context,
source: {
arrayBufferView: source,
width: 1,
height: 1,
},
});
var colorTexture1 = new Texture({
context: context,
source: {
arrayBufferView: source,
width: 1,
height: 1,
},
});
framebuffer = new Framebuffer({
context: context,
colorTextures: [colorTexture0, colorTexture1],
});
// 1 of 5. Clear default color buffer to black.
ClearCommand.ALL.execute(context);
expect(context).toReadPixels([0, 0, 0, 255]);
// 2 of 5. Render red point into color attachment 0 and green point to color attachment 1.
var vs =
"attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }";
var fs =
"#extension GL_EXT_draw_buffers : enable \n void main() { gl_FragData[0] = vec4(1.0, 0.0, 0.0, 1.0); gl_FragData[1] = vec4(0.0, 1.0, 0.0, 1.0); }";
sp = ShaderProgram.fromCache({
context: context,
vertexShaderSource: vs,
fragmentShaderSource: fs,
attributeLocations: {
position: 0,
},
});
va = new VertexArray({
context: context,
attributes: [
{
index: 0,
vertexBuffer: Buffer.createVertexBuffer({
context: context,
typedArray: new Float32Array([0, 0, 0, 1]),
usage: BufferUsage.STATIC_DRAW,
}),
componentsPerAttribute: 4,
},
],
});
var command = new DrawCommand({
primitiveType: PrimitiveType.POINTS,
shaderProgram: sp,
vertexArray: va,
framebuffer: framebuffer,
});
command.execute(context);
// 3 of 5. Verify default color buffer is still black.
expect(context).toReadPixels([0, 0, 0, 255]);
// 4 of 5. Render yellow to default color buffer by reading from previous color attachments
var vs2 =
"attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }";
var fs2 =
"uniform sampler2D u_texture0; uniform sampler2D u_texture1; void main() { gl_FragColor = texture2D(u_texture0, vec2(0.0)) + texture2D(u_texture1, vec2(0.0)); }";
var sp2 = ShaderProgram.fromCache({
context: context,
vertexShaderSource: vs2,
fragmentShaderSource: fs2,
attributeLocations: {
position: 0,
},
});
var uniformMap = {
u_texture0: function () {
return colorTexture0;
},
u_texture1: function () {
return colorTexture1;
},
};
command = new DrawCommand({
primitiveType: PrimitiveType.POINTS,
shaderProgram: sp2,
vertexArray: va,
uniformMap: uniformMap,
});
command.execute(context);
expect(context).toReadPixels([255, 255, 0, 255]);
// 5 of 5. Verify clearing multiple color attachments
var clearCommand = new ClearCommand({
color: new Color(0.0, 0.0, 0.0, 0.0),
framebuffer: framebuffer,
});
clearCommand.execute(context);
command = new DrawCommand({
primitiveType: PrimitiveType.POINTS,
shaderProgram: sp2,
vertexArray: va,
});
command.execute(context);
expect(context).toReadPixels([0, 0, 0, 255]);
sp2 = sp2.destroy();
});
it("gets the status of a complete framebuffer", function () {
framebuffer = new Framebuffer({
context: context,
colorTextures: [
new Texture({
context: context,
width: 1,
height: 1,
}),
],
depthRenderbuffer: new Renderbuffer({
context: context,
format: RenderbufferFormat.DEPTH_COMPONENT16,
width: 1,
height: 1,
}),
});
expect(framebuffer.status).toEqual(WebGLConstants.FRAMEBUFFER_COMPLETE);
});
it("gets the status of a incomplete framebuffer", function () {
if (!!window.webglStub) {
return;
}
framebuffer = new Framebuffer({
context: context,
colorTextures: [
new Texture({
context: context,
width: 1,
height: 1,
}),
],
depthRenderbuffer: new Renderbuffer({
context: context,
format: RenderbufferFormat.RGBA4,
width: 2,
height: 2,
}),
});
expect(framebuffer.status).not.toEqual(
WebGLConstants.FRAMEBUFFER_COMPLETE
);
});
it("destroys", function () {
var f = new Framebuffer({
context: context,
});
expect(f.isDestroyed()).toEqual(false);
f.destroy();
expect(f.isDestroyed()).toEqual(true);
});
it("throws when created with color texture and color renderbuffer attachments", function () {
expect(function () {
framebuffer = new Framebuffer({
context: context,
colorTextures: "not undefined",
colorRenderbuffers: "not undefined",
});
}).toThrowDeveloperError();
});
it("throws when created with depth texture and depth renderbuffer attachments", function () {
expect(function () {
framebuffer = new Framebuffer({
context: context,
depthTexture: "not undefined",
depthRenderbuffer: "not undefined",
});
}).toThrowDeveloperError();
});
it("throws when created with depth-stencil texture and depth-stencil renderbuffer attachments", function () {
expect(function () {
framebuffer = new Framebuffer({
context: context,
depthStencilTexture: "not undefined",
depthStencilRenderbuffer: "not undefined",
});
}).toThrowDeveloperError();
});
it("throws when created with depth and depth-stencil attachments", function () {
expect(function () {
framebuffer = new Framebuffer({
context: context,
depthRenderbuffer: "not undefined",
depthStencilRenderbuffer: "not undefined",
});
}).toThrowDeveloperError();
});
it("throws when created with stencil and depth-stencil attachments", function () {
expect(function () {
framebuffer = new Framebuffer({
context: context,
stencilRenderbuffer: "not undefined",
depthStencilRenderbuffer: "not undefined",
});
}).toThrowDeveloperError();
});
it("throws when created with depth and stencil attachments", function () {
expect(function () {
framebuffer = new Framebuffer({
context: context,
depthRenderbuffer: "not undefined",
stencilRenderbuffer: "not undefined",
});
}).toThrowDeveloperError();
});
it("throws when created with a color texture with a non-color pixel format", function () {
if (context.depthTexture) {
expect(function () {
framebuffer = new Framebuffer({
context: context,
colorTextures: [
new Texture({
context: context,
width: 1,
height: 1,
pixelFormat: PixelFormat.DEPTH_COMPONENT,
pixelDatatype: PixelDatatype.UNSIGNED_SHORT,
}),
],
});
}).toThrowDeveloperError();
}
});
it("throws when created with a depth texture without a DEPTH_COMPONENT pixel format", function () {
expect(function () {
framebuffer = new Framebuffer({
context: context,
depthTexture: new Texture({
context: context,
width: 1,
height: 1,
}),
});
}).toThrowDeveloperError();
});
it("throws when created with a depth-stencil texture without a DEPTH_STENCIL pixel format", function () {
expect(function () {
framebuffer = new Framebuffer({
context: context,
depthStencilTexture: new Texture({
context: context,
width: 1,
height: 1,
}),
});
}).toThrowDeveloperError();
});
it("throws when the depth test is enabled without an appropriate attachment", function () {
framebuffer = new Framebuffer({
context: context,
colorTextures: [
new Texture({
context: context,
width: 1,
height: 1,
}),
],
});
var vs =
"attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }";
var fs = "void main() { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); }";
sp = ShaderProgram.fromCache({
context: context,
vertexShaderSource: vs,
fragmentShaderSource: fs,
attributeLocations: {
position: 0,
},
});
va = new VertexArray({
context: context,
attributes: [
{
index: 0,
vertexBuffer: Buffer.createVertexBuffer({
context: context,
typedArray: new Float32Array([0, 0, 0, 1]),
usage: BufferUsage.STATIC_DRAW,
}),
componentsPerAttribute: 4,
},
],
});
expect(function () {
var command = new DrawCommand({
primitiveType: PrimitiveType.POINTS,
shaderProgram: sp,
vertexArray: va,
framebuffer: framebuffer,
renderState: RenderState.fromCache({
depthTest: {
enabled: true,
},
}),
});
command.execute(context);
}).toThrowDeveloperError();
});
it("throws when the number of color texture exceeds the number color attachments supported", function () {
expect(function () {
return new Framebuffer({
context: context,
colorTextures: new Array(ContextLimits.maximumColorAttachments + 1),
});
}).toThrowDeveloperError();
});
it("throws when the number of color renderbuffers exceeds the number color attachments supported", function () {
expect(function () {
return new Framebuffer({
context: context,
colorRenderbuffers: new Array(
ContextLimits.maximumColorAttachments + 1
),
});
}).toThrowDeveloperError();
});
it("throws when the index to getColorTexture is out of bounds", function () {
framebuffer = new Framebuffer({
context: context,
});
expect(function () {
framebuffer.getColorTexture();
}).toThrowDeveloperError();
expect(function () {
framebuffer.getColorTexture(-1);
}).toThrowDeveloperError();
expect(function () {
framebuffer.getColorTexture(ContextLimits.maximumColorAttachments + 1);
}).toThrowDeveloperError();
});
it("throws when the index to getColorRenderbuffer is out of bounds", function () {
framebuffer = new Framebuffer({
context: context,
});
expect(function () {
framebuffer.getColorRenderbuffer();
}).toThrowDeveloperError();
expect(function () {
framebuffer.getColorRenderbuffer(-1);
}).toThrowDeveloperError();
expect(function () {
framebuffer.getColorRenderbuffer(
ContextLimits.maximumColorAttachments + 1
);
}).toThrowDeveloperError();
});
it("throws when a color attachment has a floating-point datatype but the EXT_color_buffer_float extension is not supported", function () {
if (context.colorBufferFloat) {
return;
}
expect(function () {
return new Framebuffer({
context: context,
colorTextures: [
new Texture({
context: context,
width: 1,
height: 1,
pixelDatatype: PixelDatatype.FLOAT,
}),
],
});
}).toThrowDeveloperError();
});
it("throws when a color attachment has a half floating-point datatype but the EXT_color_buffer_half_float extension is not supported", function () {
if (context.colorBufferHalfFloat) {
return;
}
expect(function () {
return new Framebuffer({
context: context,
colorTextures: [
new Texture({
context: context,
width: 1,
height: 1,
pixelDatatype: PixelDatatype.HALF_FLOAT,
}),
],
});
}).toThrowDeveloperError();
});
it("fails to destroy", function () {
var f = new Framebuffer({
context: context,
});
f.destroy();
expect(function () {
f.destroy();
}).toThrowDeveloperError();
});
it("throws when there is no context", function () {
expect(function () {
return new Framebuffer();
}).toThrowDeveloperError();
});
},
"WebGL"
);