[Native] OpenGL FrameBuffer Multiple Render Targets support - possible gl.drawBuffers() not supported?


#1

Hi all, its been awhile since I last posted.

(this is with Lime 5.0.3, hxcpp 3.4.64, openfl 5.1.2)

Note - this may be purely to do with Lime and not much to do with OpenFL. Forgive me if this is the wrong place to post this low level stuff

Anyway, I have been chugging along learning OpenGL and translating book content to Haxe working sample versions. First every example (and the website extras) from WebGL Programming Guide by Matsuda Lea, and now working on OpenGL 4 Shading Language Cookbook (which I know 4 is kind of high for cross-platform but I am making do and know enough by now about #version differences to make it work so far)

Anyways I have hit a bit of an impasse.

I am working on getting a FrameBuffer with Multiple Render Targets sample up and running, and am having trouble getting the FrameBuffer to render to more than just one COLOR_ATTACHMENT# (in this case, 0 works).

I was able to get a single texture-bound framebuffer example to work, and even went so far as to extend it as a sort of ping-pong rendering using two FBOs with single texture each.

Where I am at now, is only the first texture bound to the FBO will be rendered to, when I am attempting to bind 3 (one each for storing Position data, Normal data, and diffuse/color data) however I can only get the first to actually render to texture, and this ends up incorrect which I’ll describe below. (in short, fragment shader seems to only respect layout (location = 0) out vec4 FragColor, and the texture bound to FBO with COLOR_ATTACHMENT0 is the only texture drawn-to)

The way I am testing the end-result is by simply, using a if statement branch in my ubershader (bad practice I know, this is just for prototype reasons), I draw from a selected texture to a simple quad.

Frag shader setup:

	layout (location = 0) out vec4 FragColor;
	layout (location = 1) out vec4 PositionData;
	layout (location = 2) out vec4 NormalData;
	layout (location = 3) out vec4 ColorData;
        
	// actual texture
	uniform sampler2D u_sampler; // ends up bound to TEXTURE0 when drawing-from
	// g-buffer textures
	uniform sampler2D u_positionTex; // ends up bound to TEXTURE0+1 when drawing-from
	uniform sampler2D u_normalTex; // ends up bound to TEXTURE0+12 when drawing-from
	uniform sampler2D u_colorTex; // ends up bound to TEXTURE0+13 when drawing-from

	void main() {
		//if render mode is store in g-buffers...
		PositionData = vec4(Position, 1.0);
		NormalData = vec4(Normal, 1.0);
		ColorData = texture2D(u_Sampler, v_TexCoord);
		FragColor = texture2D(u_Sampler, v_TexCoord);

		//if render mode is draw to quad... (( and FBO is unbound ))
		textureColor = texture2D(selectedTexture, v_TexCoord)  
                // /\  where selected texture ends up either u_sampler, u_positionTex, u_normalTex, or u_colorTex
		FragColor = textureColor;

         ...
         }
        

When binding the textures to the FBO I use the following call:

gl.framebufferTexture2D(gl.FRAMEBUFFER, tempTextureInfoContainer.gl_ColorAttachment, 
					gl.TEXTURE_2D, tempTextureInfoContainer.texture, 0);

where tempTextureInfoContainer.gl_ColorAttachment, in order of the textures passed in, is COLOR_ATTACHMENT0, 1, and 2 respectively.

As mentioned above, only one texture ends up being drawn to, and that is the first texture I bind to the FBO.
However, what ends up being drawn to that first texture, is FragColor in layout = 0. Changing what is drawn to it to hard color (green) or one of the other values (such as storing the normal) does propagate to the texture at TEXTURE1 (bound to FBO using COLOR_ATTACHMENT0 ie layout = 0) and eventually to the test-draw to the quad.

After wrestling with this off and on for about a week I am at an impasse. I believe I have narrowed it down to the following call, which appears to do nothing:

var drawBuffersArray:Array<Int> = gl.NONE, [gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2];
gl.drawBuffers(drawBuffersArray);  //does nothing

The purpose of drawBuffers is to connect the textures (bound to the FBO) to the fragment shaders output variables (a la 0, 1, and 2) so that they get drawn to. The default is a single entry COLOR_ATTACHMENT0, which is why I think single-texture FBO’s do work

in fact I can pass any number of, and as high an Int value into that array that I want (ie values that surpass MAX_DRAW_BUFFERS, even negative values) and there are no errors thrown. Passing a variety of values to this does not change anything.

This leads me to believe this function gl.drawBuffers(buffers:Array) is (as of yet) unsupported on Native. (the function is a stub in NativeGLRenderContext.hx) Is this correct? If so, are there plans to implement support in the future? (I’ve peeked into the latest in Lime 5.3.0 but it still looks like a stub)

If it is in fact supported, are there any ideas as to what I am doing wrong then? I am at a loss and Deferred Rendering is pretty important for my engine I am building. (going to be using a ton of lights)

Anyways, thank you for your time!


#2

Lime 4 redesigned our OpenGL layer, to go beyond only a WebGL 1 API, and into being able to better support additional OpenGL APIs (hopefully in a sane way).

The focus was on adding WebGL 2 support on HTML5, so I believe your code would work on a WebGL 2 compatible browser context, but you are right – NativeGLRenderContext had an empty drawBuffers implementation, which means that the native binding was missing

I have just added this method, but there are more that we need:

If anyone wants to help implement more, help would be accepted :smile:

In the meantime, I’d be interested if this helps. Do you think you would be able to try a development version of Lime? https://github.com/openfl/lime#building-from-source


#3

I appreciate it! Unfortunately, it still does not seem to work. I had to grab openfl 6.0.1 as well. I see that gl.drawBuffers() is now populated in the two prior files that were just stubs before. However, it still does not seem to have any effect. That is, even when I pass what should be invalid values, it does not throw a gl error, and valid values do not change the COLOR_ATTACHMENT##s that are drawn to, as it remains as only outputting to COLOR_ATTACHMENT0

NativeGLRenderContext.hx

	public function drawBuffers (buffers:Array<Int>):Void {
		
		#if (lime_cffi && lime_opengl && !macro)
		NativeCFFI.lime_gl_draw_buffers (buffers);
		#end
		
	}

NativeCFFI.hx

	@:cffi private static function lime_gl_draw_buffers (buffers:Dynamic):Void;

I dont know too much about cpp or haxe compiles to it, but I got brave and peeked further under the hood, to the below

\bin\windows\cpp\debug\obj\src\lime\graphics\opengl\GL.cpp

and it looks like it might not be supported deep down there, as the function Dynamic() is all that is called, as appears with some of the other GL methods that I think are in the works:

STATIC_HX_DEFINE_DYNAMIC_FUNC4(GL_obj,drawArraysInstanced,(void))

Void GL_obj::drawBuffers( Array< int > buffers){
{
		Dynamic();
	}
return null();
}

I wish I knew more about the deep backend myself, as then I would be able to contribute with my increasing opengl knowledge. I may consider it in the future (year or two) after I have completed this project and have at least something done for the portfolio. Every couple years or so I dive deep into some new territory, and learning C++ and how it is generated / linked-to thru haxe seems a logical next step. (the path has been AS3 -> Haxe -> OpenFL -> Web/OpenGL so far, with a sidequest, ie ‘dayjob’ of js,php,pgsql,html5,css for a year and half, till bout a year ago), so its pretty well rounded but it seems I go deeper towards the boilerplate each cycle, ha. Here’s hoping I don’t end up in assembly!

In any case, though I would absolutely love gl.drawBuffers() implementation, and further desktop gl support, it is not project breaking thankfully. I can see a workaround, though it comes at a performance cost. I can do multiple render passes through a single output (default) FBO, binding a different single texture, and drawing method to colorattachment0 as needed (like with the ping-pong 2x FBO example). Then when I have all the information stored in the textures (probably 4 extra render calls later) thennn I can do the lighting/shading pass with a different shader set. This does have a performance cost, though hopefully not too great. I fear a hitting a bottleneck down the line as the project evolves though, especially as I add custom post processing effects which is my main reason for diving into OpenGL.

I have not had a chance to try it targeting html/browser yet, as my intended target here is desktop so far, so I cannot really vouch for if drawbuffers is supported in webgl2 context. Maybe someone else can vouch for this?

Anyways, thanks for all you and the team at openfl and lime do! Its great that the opengl support is growing and I love the move to contexts. I jumped from 2.9.0 lime to 5.1.2 for the extra features which you might imagine was a big leap, but definitely worth it! Hopefully I can make contributions sometime down the line.


#4

After our conversation (the first time), I went back and looked into supported GLES3 APIs on native. Good news! I believe it is working, currently implemented for Linux and Emscripten, but I hope to enable it in a way that does not require newer GL libraries on Windows or Mac:

For example, here’s drawBuffers on the C++ side:

I disabled LIME_GLES3_API on Windows and Mac, because (depending on the version of the compiler and system GL library) it would complain about some missing symbols when linking. This is solvable, but just requires a bit more work, but on your machine, it should be possible to add LIME_GLES_API under the “project/src/graphics/opengl/OpenGL.h” file (based on the platform) then comment out a couple calls it complains about if your system doesn’t have them :slight_smile:

Anyway, just let me know if you want to try this out or how I can help


#5

I am sorry I did not reply to this post - but I wanted to say real quick that the development build of Lime did in fact work fantastically (targeting windows only here though) and I was up and away rendering to multiple textures at once. The possibilities are now endless! Thank you again so much for that, you rock.

As of current, I have not tested that project on the latest versions of Lime and OpenFL though, but I am hoping those additions made it in!