<commit_before>// framebuffer - FrameBuffer objects
// Part of Stardazed TX
// (c) 2015 by <NAME> - @zenmumbler

/// <reference path="framebuffer-desc.ts"/>
/// <reference path="texture.ts"/>
/// <reference path="rendercontext.ts"/>

namespace sd.render {

	var fboBugs = {
		mustHaveAColourAtt: <boolean>null
	};

	function fboMustHaveAColourAttachment(rc: RenderContext) {
		if (fboBugs.mustHaveAColourAtt === null) {
			var gl = rc.gl;
			var fboBinding = gl.getParameter(gl.FRAMEBUFFER_BINDING);

			var fbo = gl.createFramebuffer();
			gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
		
			// -- create and attach depth buffer
			var depthBuf = gl.createRenderbuffer();
			gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuf);
			gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 160, 120);
			gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuf);

			// -- specify empty draw buffer list
			if (rc.extDrawBuffers)
				rc.extDrawBuffers.drawBuffersWEBGL([gl.NONE]);

			// This bug occurs on Mac OS X in Safari 9 and Chrome 45, it is due to faulty
			// setup of DRAW and READ buffer bindings in the underlying GL4 context.
			// Bugs have been filed.		
			var fbStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
			fboBugs.mustHaveAColourAtt = (fbStatus != gl.FRAMEBUFFER_COMPLETE);

			gl.bindFramebuffer(gl.FRAMEBUFFER, fboBinding);
			gl.bindTexture(gl.TEXTURE_2D, null);

			gl.deleteFramebuffer(fbo);
			gl.deleteRenderbuffer(depthBuf);
		}

		return fboBugs.mustHaveAColourAtt;
	}


	export function allocateTexturesForFrameBuffer(rc: RenderContext, desc: FrameBufferAllocationDescriptor): FrameBufferDescriptor {
		var fbDesc = makeFrameBufferDescriptor();

		var width = desc.width;
		var height = desc.height;
	
		// -- default to viewport size if not explicitly specified
		if (width == 0 && height == 0) {
			width = rc.gl.drawingBufferWidth;
			height = rc.gl.drawingBufferHeight;
		}
	
		// -- colour
		if ((desc.colourPixelFormats[0] == PixelFormat.None) && fboMustHaveAColourAttachment(rc)) {
			// work around FBO bug, see fboMustHaveAColourAttachment function
			desc.colourPixelFormats[0] = PixelFormat.RGB8;
		}

		for (var colourAttIndex = 0; colourAttIndex < desc.colourPixelFormats.length; ++colourAttIndex) {
			if (desc.colourPixelFormats[colourAttIndex] != PixelFormat.None) {
				var texDesc = makeTextureDescriptor();
				texDesc.textureClass = TextureClass.Tex2D;
				texDesc.dim.width = width;
				texDesc.dim.height = height;
				texDesc.sampling.repeatS = texDesc.sampling.repeatT = TextureRepeatMode.ClampToEdge;
				texDesc.sampling.mipFilter = TextureMipFilter.None;
				texDesc.pixelFormat = desc.colourPixelFormats[colourAttIndex];
				texDesc.usageHint = desc.colourUsageHints[colourAttIndex];

				var attachment = fbDesc.colourAttachments[colourAttIndex];
				attachment.texture = new Texture(rc, texDesc);
			}
		}

		// -- depth & stencil
		var combinedFormat = PixelFormat.None;

		assert(desc.depthPixelFormat == PixelFormat.None ||
			pixelFormatIsDepthFormat(desc.depthPixelFormat) ||
			pixelFormatIsDepthStencilFormat(desc.depthPixelFormat));
		assert(desc.stencilPixelFormat == PixelFormat.None ||
			pixelFormatIsStencilFormat(desc.stencilPixelFormat) ||
			pixelFormatIsDepthStencilFormat(desc.stencilPixelFormat));

		// -- check if we can use a combined depth/stencil format
		if (pixelFormatIsDepthStencilFormat(desc.depthPixelFormat)) {
			// explicit combined format
			assert(desc.depthPixelFormat == desc.stencilPixelFormat);
			assert(desc.depthUsageHint == desc.stencilUsageHint);
			combinedFormat = desc.depthPixelFormat;
		}
		else {
			// if depth is not a DS format, then stencil cannot be a DS format either
			assert(! pixelFormatIsDepthStencilFormat(desc.stencilPixelFormat));

			// in order to be combined, the usage hints must be the same
			if (desc.depthUsageHint == desc.stencilUsageHint) {
				// check for available depth/stencil format combinations
				if (desc.stencilPixelFormat == PixelFormat.Stencil8) {
					if (desc.depthPixelFormat == PixelFormat.Depth24I) {
						combinedFormat = PixelFormat.Depth24_Stencil8;
					}
				}
			}
		}

		// -- create the texture(s)
		var dsTex = makeTextureDescriptor();
		dsTex.textureClass = TextureClass.Tex2D;
		dsTex.dim.width = width;
		dsTex.dim.height = height;
		dsTex.sampling.repeatS = dsTex.sampling.repeatT = TextureRepeatMode.ClampToEdge;
		dsTex.sampling.mipFilter = TextureMipFilter.None;

		if (combinedFormat != PixelFormat.None) {
			dsTex.pixelFormat = combinedFormat;
			dsTex.usageHint = desc.depthUsageHint;
			var depthStencil = new Texture(rc, dsTex);

			fbDesc.depthAttachment.texture = depthStencil;
			fbDesc.stencilAttachment.texture = depthStencil;
		}
		else {
			if (desc.depthPixelFormat != PixelFormat.None) {
				dsTex.pixelFormat = desc.depthPixelFormat;
				dsTex.usageHint = desc.depthUsageHint;
				fbDesc.depthAttachment.texture = new Texture(rc, dsTex);
			}
			if (desc.stencilPixelFormat != PixelFormat.None) {
				dsTex.pixelFormat = desc.stencilPixelFormat;
				dsTex.usageHint = desc.stencilUsageHint;
				fbDesc.stencilAttachment.texture = new Texture(rc, dsTex);
			}
		}

		return fbDesc;
	}


	export class FrameBuffer {
		private attachmentDesc_: FrameBufferDescriptor;
		private fbo_: WebGLFramebuffer;
		private width_ = 0;
		private height_ = 0;


		private attachTexture(glAttachment: number, attachment: AttachmentDescriptor) {
			var gl = this.rc.gl;

			if (attachment.texture.target == gl.RENDERBUFFER) {
				assert(attachment.level == 0, "renderbuffers do not have mipmaps");
				assert(attachment.layer == 0, "renderbuffers do not have layers");

				gl.framebufferRenderbuffer(gl.FRAMEBUFFER, glAttachment, gl.RENDERBUFFER, <WebGLRenderbuffer>attachment.texture.resource);
			}
			else {
				var tex = <WebGLTexture>attachment.texture.resource;
				assert(attachment.level == 0, "WebGL 1 does not allow mapping of texture level > 0");
				assert(attachment.level < attachment.texture.mipmaps);

				var glTarget = gl.TEXTURE_2D;
				if (attachment.texture.textureClass == TextureClass.TexCube) {
					assert(attachment.layer >= 0 && attachment.layer <= 5, "layer is not a valid CubeMapFace index");
					glTarget = gl.TEXTURE_CUBE_MAP_POSITIVE_X + attachment.layer;
				}

				gl.framebufferTexture2D(gl.FRAMEBUFFER, glAttachment, glTarget, tex, attachment.level);
			}
		}


		constructor(private rc: RenderContext, desc: FrameBufferDescriptor) {
			var gl = rc.gl;
			var fbo = this.fbo_ = gl.createFramebuffer();
			gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);

			// -- deep copy of descriptor
			this.attachmentDesc_ = {
				colourAttachments: desc.colourAttachments.map((att) => cloneStruct(att)),
				depthAttachment: cloneStruct(desc.depthAttachment),
				stencilAttachment: cloneStruct(desc.stencilAttachment)
			};

			var anyTexture: Texture = null;

			// -- colour
			var drawBuffers = desc.colourAttachments.map((attachment, attIndex) => {
				if (attachment.texture) {
					anyTexture = attachment.texture;
					var glAttachment = rc.extDrawBuffers ? (rc.extDrawBuffers.COLOR_ATTACHMENT0_WEBGL + attIndex) : rc.gl.COLOR_ATTACHMENT0;
					this.attachTexture(glAttachment, attachment);
					return glAttachment;
				}
				else {
					return gl.NONE;
				}
			});

			// -- setup the draw buffers to mimic the colour attachments
			// -- which is required in WebGL
			if (rc.extDrawBuffers)
				rc.extDrawBuffers.drawBuffersWEBGL(drawBuffers);


			// -- depth and/or stencil
			var depthTex = desc.depthAttachment.texture;
			var stencilTex = desc.stencilAttachment.texture;

			if (depthTex) {
				anyTexture = depthTex;
				assert(desc.depthAttachment.level == 0);<commit_msg>framebuffer: some nullables<commit_after>// framebuffer - FrameBuffer objects
// Part of Stardazed TX
// (c) 2015 by <NAME> - @zenmumbler

/// <reference path="framebuffer-desc.ts"/>
/// <reference path="texture.ts"/>
/// <reference path="rendercontext.ts"/>

namespace sd.render {

	var fboBugs = {
		mustHaveAColourAtt: <(boolean | null)>null
	};

	function fboMustHaveAColourAttachment(rc: RenderContext) {
		if (fboBugs.mustHaveAColourAtt === null) {
			var gl = rc.gl;
			var fboBinding = gl.getParameter(gl.FRAMEBUFFER_BINDING);

			var fbo = gl.createFramebuffer();
			gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
		
			// -- create and attach depth buffer
			var depthBuf = gl.createRenderbuffer();
			gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuf);
			gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 160, 120);
			gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuf);

			// -- specify empty draw buffer list
			if (rc.extDrawBuffers)
				rc.extDrawBuffers.drawBuffersWEBGL([gl.NONE]);

			// This bug occurs on Mac OS X in Safari 9 and Chrome 45, it is due to faulty
			// setup of DRAW and READ buffer bindings in the underlying GL4 context.
			// Bugs have been filed.		
			var fbStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
			fboBugs.mustHaveAColourAtt = (fbStatus != gl.FRAMEBUFFER_COMPLETE);

			gl.bindFramebuffer(gl.FRAMEBUFFER, fboBinding);
			gl.bindTexture(gl.TEXTURE_2D, null);

			gl.deleteFramebuffer(fbo);
			gl.deleteRenderbuffer(depthBuf);
		}

		return fboBugs.mustHaveAColourAtt;
	}


	export function allocateTexturesForFrameBuffer(rc: RenderContext, desc: FrameBufferAllocationDescriptor): FrameBufferDescriptor {
		var fbDesc = makeFrameBufferDescriptor();

		var width = desc.width;
		var height = desc.height;
	
		// -- default to viewport size if not explicitly specified
		if (width == 0 && height == 0) {
			width = rc.gl.drawingBufferWidth;
			height = rc.gl.drawingBufferHeight;
		}
	
		// -- colour
		if ((desc.colourPixelFormats[0] == PixelFormat.None) && fboMustHaveAColourAttachment(rc)) {
			// work around FBO bug, see fboMustHaveAColourAttachment function
			desc.colourPixelFormats[0] = PixelFormat.RGB8;
		}

		for (var colourAttIndex = 0; colourAttIndex < desc.colourPixelFormats.length; ++colourAttIndex) {
			if (desc.colourPixelFormats[colourAttIndex] != PixelFormat.None) {
				var texDesc = makeTextureDescriptor();
				texDesc.textureClass = TextureClass.Tex2D;
				texDesc.dim.width = width;
				texDesc.dim.height = height;
				texDesc.sampling.repeatS = texDesc.sampling.repeatT = TextureRepeatMode.ClampToEdge;
				texDesc.sampling.mipFilter = TextureMipFilter.None;
				texDesc.pixelFormat = desc.colourPixelFormats[colourAttIndex];
				texDesc.usageHint = desc.colourUsageHints[colourAttIndex];

				var attachment = fbDesc.colourAttachments[colourAttIndex];
				attachment.texture = new Texture(rc, texDesc);
			}
		}

		// -- depth & stencil
		var combinedFormat = PixelFormat.None;

		assert(desc.depthPixelFormat == PixelFormat.None ||
			pixelFormatIsDepthFormat(desc.depthPixelFormat) ||
			pixelFormatIsDepthStencilFormat(desc.depthPixelFormat));
		assert(desc.stencilPixelFormat == PixelFormat.None ||
			pixelFormatIsStencilFormat(desc.stencilPixelFormat) ||
			pixelFormatIsDepthStencilFormat(desc.stencilPixelFormat));

		// -- check if we can use a combined depth/stencil format
		if (pixelFormatIsDepthStencilFormat(desc.depthPixelFormat)) {
			// explicit combined format
			assert(desc.depthPixelFormat == desc.stencilPixelFormat);
			assert(desc.depthUsageHint == desc.stencilUsageHint);
			combinedFormat = desc.depthPixelFormat;
		}
		else {
			// if depth is not a DS format, then stencil cannot be a DS format either
			assert(! pixelFormatIsDepthStencilFormat(desc.stencilPixelFormat));

			// in order to be combined, the usage hints must be the same
			if (desc.depthUsageHint == desc.stencilUsageHint) {
				// check for available depth/stencil format combinations
				if (desc.stencilPixelFormat == PixelFormat.Stencil8) {
					if (desc.depthPixelFormat == PixelFormat.Depth24I) {
						combinedFormat = PixelFormat.Depth24_Stencil8;
					}
				}
			}
		}

		// -- create the texture(s)
		var dsTex = makeTextureDescriptor();
		dsTex.textureClass = TextureClass.Tex2D;
		dsTex.dim.width = width;
		dsTex.dim.height = height;
		dsTex.sampling.repeatS = dsTex.sampling.repeatT = TextureRepeatMode.ClampToEdge;
		dsTex.sampling.mipFilter = TextureMipFilter.None;

		if (combinedFormat != PixelFormat.None) {
			dsTex.pixelFormat = combinedFormat;
			dsTex.usageHint = desc.depthUsageHint;
			var depthStencil = new Texture(rc, dsTex);

			fbDesc.depthAttachment.texture = depthStencil;
			fbDesc.stencilAttachment.texture = depthStencil;
		}
		else {
			if (desc.depthPixelFormat != PixelFormat.None) {
				dsTex.pixelFormat = desc.depthPixelFormat;
				dsTex.usageHint = desc.depthUsageHint;
				fbDesc.depthAttachment.texture = new Texture(rc, dsTex);
			}
			if (desc.stencilPixelFormat != PixelFormat.None) {
				dsTex.pixelFormat = desc.stencilPixelFormat;
				dsTex.usageHint = desc.stencilUsageHint;
				fbDesc.stencilAttachment.texture = new Texture(rc, dsTex);
			}
		}

		return fbDesc;
	}


	export class FrameBuffer {
		private attachmentDesc_: FrameBufferDescriptor;
		private fbo_: WebGLFramebuffer;
		private width_ = 0;
		private height_ = 0;


		private attachTexture(glAttachment: number, attachment: AttachmentDescriptor) {
			var gl = this.rc.gl;

			if (attachment.texture.target == gl.RENDERBUFFER) {
				assert(attachment.level == 0, "renderbuffers do not have mipmaps");
				assert(attachment.layer == 0, "renderbuffers do not have layers");

				gl.framebufferRenderbuffer(gl.FRAMEBUFFER, glAttachment, gl.RENDERBUFFER, <WebGLRenderbuffer>attachment.texture.resource);
			}
			else {
				var tex = <WebGLTexture>attachment.texture.resource;
				assert(attachment.level == 0, "WebGL 1 does not allow mapping of texture level > 0");
				assert(attachment.level < attachment.texture.mipmaps);

				var glTarget = gl.TEXTURE_2D;
				if (attachment.texture.textureClass == TextureClass.TexCube) {
					assert(attachment.layer >= 0 && attachment.layer <= 5, "layer is not a valid CubeMapFace index");
					glTarget = gl.TEXTURE_CUBE_MAP_POSITIVE_X + attachment.layer;
				}

				gl.framebufferTexture2D(gl.FRAMEBUFFER, glAttachment, glTarget, tex, attachment.level);
			}
		}


		constructor(private rc: RenderContext, desc: FrameBufferDescriptor) {
			var gl = rc.gl;
			var fbo = this.fbo_ = gl.createFramebuffer()!; // FIXME: verify resource creation
			gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);

			// -- deep copy of descriptor
			this.attachmentDesc_ = {
				colourAttachments: desc.colourAttachments.map((att) => cloneStruct(att)),
				depthAttachment: cloneStruct(desc.depthAttachment),
				stencilAttachment: cloneStruct(desc.stencilAttachment)
			};

			var anyTexture: (Texture | null) = null;

			// -- colour
			var drawBuffers = desc.colourAttachments.map((attachment, attIndex) => {
				if (attachment.texture) {
					anyTexture = attachment.texture;
					var glAttachment = rc.extDrawBuffers ? (rc.extDrawBuffers.COLOR_ATTACHMENT0_WEBGL + attIndex) : rc.gl.COLOR_ATTACHMENT0;
					this.attachTexture(glAttachment, attachment);
					return glAttachment;
				}
				else {
					return gl.NONE;
				}
			});

			// -- setup the draw buffers to mimic the colour attachments
			// -- which is required in WebGL
			if (rc.extDrawBuffers)
				rc.extDrawBuffers.drawBuffersWEBGL(drawBuffers);


			// -- depth and/or stencil
			var depthTex = desc.depthAttachment.texture;
			var stencilTex = desc.stencilAttachment.texture;

			if (depthTex) {
				anyTexture = depthTex;
				assert(desc.depthAttachment.level == 0);
{'fraction_non_alphanumeric': 0.10710135571336346, 'fraction_numerical': 0.005810200129115558, 'mean_word_length': 3.9874436574372183, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3469123', 'n_tokens_mistral': 5712, 'n_tokens_neox': 4830, 'n_words': 1023}