bgfx module

Ferdi

Member
3rd Party Target Dev
Joined
Jun 21, 2017
Very interesting to read and I am curious about the outcome.
Hopefully the outcome is an easy to use, user friendly module. Currently it is not.

Texture

I got texture working, but I broke SetColor. I am pretty sure multiplying color and texture in the fragment shader is correct. It works on DrawLine. And it works when I do it in OpenGL, but not it does not work in bgfx.

Code:
gl_FragColor = v_color0 * texture2D(s_texColor, v_texcoord0);

Screenshot:

mojo2texture.png

I will look into it when I am doing blending / shaders, the main thing is I know texture is working. I still don't know how to do mipmap, I need to look into that.

GLFW + BGFX

This is really easy to implement. I just have to change glfwgame.cpp, main.h and makefile. In glfwgame.cpp I removed glfwMakeContextCurrent, so it doesn't create OpenGL, and I init bgfx. In main.h I had to include bx, bgfx and glfw3native header files. And makefile changes is mostly include directories and libraries. Only ~30 LOC in glfwgame.cpp and main.h. This should be a very stable target for desktop.
 

Ferdi

Member
3rd Party Target Dev
Joined
Jun 21, 2017
Quick update. I am stuck on shader. I was going to create and compile a bin file for all the permutation. But Mojo 2 creates a different shader code for each light:

Code:
        Local defs:=""
        defs+="#define NUM_LIGHTS "+numLights+"~n"
       
         Local vshader:=glCompile( GL_VERTEX_SHADER,defs+_vsource )
        Local fshader:=glCompile( GL_FRAGMENT_SHADER,defs+_fsource)
So if I do all the permutation for every single light, there will be too many shader bin files.

While thinking about that I did blending, scissors, the define file and macros.
 

dawlane

Active Member
CX Code Contributor
Joined
Jun 21, 2017
Wasn't there a shader tool compiler in the sources. With a bit of know how it should be too hard to modify the project files to execute it and build on the fly.
 
Last edited:

MikeHart

Administrator
Joined
Jun 19, 2017
Location
Germany
It actually uses the GLSL compiler

From mojo2's glutil.cxs

Code:
Function glCompile:Int( type:Int,source:String )
   
    #If TARGET<>"glfw" Or GLFW_USE_ANGLE_GLES20
        source="precision mediump float;~n"+source
    #Endif
   
    Local shader:=glCreateShader( type )
    glShaderSource shader,source
    glCompileShader shader
    glGetShaderiv shader,GL_COMPILE_STATUS,tmpi
    If Not tmpi[0]
        Print "Failed to compile fragment shader:"+glGetShaderInfoLog( shader )
        Local lines:=source.Split( "~n" )
        For Local i:=0 Until lines.Length
            Print (i+1)+":~t"+lines[i]
        Next
        Error "Compile fragment shader failed"
    Endif
    Return shader

End
 

Ferdi

Member
3rd Party Target Dev
Joined
Jun 21, 2017
Yeah, it creates all the permutations for every number of lights and compiles (glCompile and glLink from glutil) all shader permutations at runtime. It is very cryptic codes. I was trying to figure out why it was done this way.

So, I created a mojo 2 shader where I removed the lights. That is, one shader code for number of light permutations. It is an optimization. If it is a single shader (where light permutations are removed) bouncyaliens.cxs drop below 60fps at about ~1000. But with the lights calculation removed, the bouncyaliens.cxs drop below 60fps at about 5000+. That optimization is an increase of 500%!

I will look at compiling shader at runtime in bgfx. I thought it was not possible because it uses C macros + this sentence:

D3D9 and D3D11 shaders can be only compiled on Windows.
 

Ferdi

Member
3rd Party Target Dev
Joined
Jun 21, 2017
I have managed to get the directlight mojo 2 example working. But don't get too excited, this is still the slow/unoptimize shader. So I managed to remove all the lights optimization from the glsl code, and this simplified the glsl code greatly. Then I managed to port that code over to bgfx shader code. In theory this is 500% slower. Here is a screenshot:

mojo2directlightexample.png

Was going to post some shader codes, but it is too long.

My to do list:
1. read/write pixels
2. frame buffers
3. mipmaps (I know roughly what to do here, I have to use bimg to create the mipmaps and then pass it to bgfx)
4. compile bgfx shaders at runtime (this might be the biggest unknown now)
5. run all examples
 

Ferdi

Member
3rd Party Target Dev
Joined
Jun 21, 2017
I had a weird bug to fix. I was overwriting vertex buffer. Took me a while to figure it out. Dynamic vertex buffer in bgfx can only be used once in a single frame, if it is updated during the frame, then the data is updated. So I was doing this:

Code:
bgfxMakeRef( rs_vboMem, _data, offset, size )
bgfxUpdateDynamicVertexBuffer( rs_vbo, 0, rs_vboMem )
The above code overwrites the vertex buffer. I had to do the following, so it does not overwrite the vertex buffer:

Code:
bgfxMakeRef( rs_vboMem[vboIndex], _data, offset, size )
bgfxUpdateDynamicVertexBuffer( rs_vbo[vboIndex], 0, rs_vboMem[vboIndex] )
That was a hard one to figure out. The solution is still not "clean/elegent". I know the problem, hopefully I find a better solution for it.
 

Ferdi

Member
3rd Party Target Dev
Joined
Jun 21, 2017
For view port, canvas, and render target, bgfx uses an id, which is an integer. So to draw to a view you have to specify which id (0, 1, 2 etc). This is not the case for mojo 2 which set the view, canvas, and render target to draw. For example Canvas.SetViewport and Canvas.SetRenderTarget. Also you can define a number of canvas, like canvas1, canvas2, canvas3 etc. I have been trying to implement a solution for this for past several days, but there is always a scenario where it breaks. And there were other problems, like another overwrite problem. Anyway I will try to find a robust solution. If all else fail I may need to give the function SetViewId, so the user has to manually set which view to draw to - not good.

Anyway to some good news:

This is very "theoretical", so don't get your hope up too much. These numbers are all for my PC. For the example bouncyaliens, bgfx can so ~4000 aliens before the frame rate goes below 60 FPS. As shown in the screenshot. This is using the compiled slow bump shader. I have written above in my previous post, for the slow shader GLES20, I get ~1000, and for the optimize fast shader GLES20 I get ~5000, before the FPS goes below 60. So it seems Direct3D 11 is about 400% faster then GLES20. I will do the test again at the very end. This is using a buggy version of bgfx mojo2 code.

mojo2bouncyaliens.png

Here is the rendertoimage example working, but there is a problem at the bottom left hand corner. I don't know why that is white rectangle. This test drawing to image.

mojo2rendertoimage.png

Here is the writepixels example working, this test write pixels.

mojo2writepixels.png
 

Ferdi

Member
3rd Party Target Dev
Joined
Jun 21, 2017
So bfgx doesn't have multiple render targets?
Sorry for the very late reply Mike. I tried to put an automatic view id fix, and it just breaks when I tried to get the renderdemo.cxs working. It does have multiple render targets, but it is different to how Open GL works. Let me explain the problem. In Mojo 1 if I do this

Code:
SetScissor(0, 0, 320, 240)
DrawCircle(320, 240, 50)

SetScissor(320, 0, 320, 240)
DrawCircle(480, 240, 50)

SetScissor(320, 240, 320, 240)
DrawCircle(320, 320, 50)
In Mojo 1, I will get a screen like this:

mojo scissors.png

In bgfx, I will get a screen like this:

bgfx scissors.png

This is because only the last SetScissor is executed the other 2 SetScissor commands are overwritten by this last SetScissor. Bgfx doesn't draw anything or does SetScissor, bgfx just buffers those commands.

To do this correctly in bgfx, we have to use view id. View id is an integer.

Code:
' the first argument of each function is view id
SetScissor(0, 0, 0, 320, 240)
DrawCircle(0, 320, 240, 50)

SetScissor(1, 320, 0, 320, 240)
DrawCircle(1, 480, 240, 50)

SetScissor(2, 320, 240, 320, 240)
DrawCircle(2, 320, 320, 50)
I hope that make sense. Please ask questions if it does not.

Why does bgfx do it this way? I am just guessing, but I think because it draws using multi-threading. Again I am guessing, that may be also the reason why I was getting 400% speed increase for bouncyaliens, because I have 4 cores in my system, not because of Direct3D 11.

I think, Mojo 1 is possible if there is a view id for every function as the first argument, which feels like a step down from Mojo 1. Then you also have to explain to Cerberus users you have to use a different view id for a different SetScissor call. I worry this is confusing to the end user.

A new mojo is also a possibility, but you still have to explain to the end users you can not have 2 SetScissor call using the same view id.

I don't think I can get Mojo 2 working with bgfx. What do you guys want to do?
 

MikeHart

Administrator
Joined
Jun 19, 2017
Location
Germany
I don't think I can get Mojo 2 working with bgfx. What do you guys want to do?
Bummer :( Maybe it is better to create a new framework then ( Hades hint hint :D ). This way it would not be constraint by the mojo way of coding.
 

Holzchopf

Moderator
Joined
Jul 31, 2017
Location
Bern, Switzerland
I agree. I'm no expert on this field, but wouldn't wrapping one framework with its specific workflow to another with its own specific and in some ways very different workflow lead to overheads anyway? Then forcing the programmer to do it the framework's way directly rather than translating the programmers gibberish into framework instructions at runtime would certainly be the better choice.
 

Ferdi

Member
3rd Party Target Dev
Joined
Jun 21, 2017
My thinking is writing new code is expensive. So if I can provide a wrapper that works for everyone projects, then no code has to change. But it looks like I am going to break everyone codes instead.

I have no opinion on the new framework name (Hades sounds good, because Cerberus and Hades :) ) or how the APIS is going to look, but we all have to agree on it.

But, if other people in the community wants to give it a name for the framework speak now, we can have a vote?

Do you want the APIS to be more like Mojo 1 or Mojo 2 or something else? What I mean by that is this, in Mojo 1:

Code:
DrawCircle(100, 100, 50)
DrawRect(200, 200, 300, 80)
In Mojo 2 it looks like this, there is that canvas:

Code:
Field canvas:Canvas = New Canvas()
...
canvas.DrawCircle(100, 100, 50)
canvas.DrawRect(200, 200, 300, 80)
I prefer Mojo 1 myself, more straight forward. What does other prefer?

Also how is the view id going to work, do you have to specify the view id for every command, like this:

Code:
Function SetScissors:Void( viewId:Int, x:Int, y:Int, width:Int, height:Int )
Function DrawCircle:Void( viewId:Int, x:Int, y:Int, radius:Int )
Function DrawRect:Void( viewId:Int, x:Int, y:Int, width:Int, height:Int )
So your code will look like this:

Code:
SetScissors( 0, 0, 0, 320, 240 )
DrawCircle( 0, 100, 100, 50 )
DrawRect( 0, 200, 100, 200, 200 )

SetScissors( 1, 320, 0, 320, 240 )
DrawCircle( 1, 480, 100, 50 )
DrawRect( 1, 480, 200, 200, 200 )
Or do you want to have a GetNextViewId() (or SetViewid()?), so your code looks like this:

Code:
GetNextViewId()
SetScissors( 0, 0, 320, 240 )
DrawCircle( 100, 100, 50 )
DrawRect( 200, 100, 200, 200 )

GetNextViewId()
SetScissors( 320, 0, 320, 240 )
DrawCircle( 480, 100, 50 )
DrawRect( 480, 200, 200, 200 )
Or something else?

There are advantages and disadvantages for both. There are probably more questions like these. We will start with these questions.
 

AndyAndroid

Member
Joined
Jun 28, 2017
@Ferdi - It looks like you can also set the view mode to sequential and use a single view with bgfx. Even though this is less efficient you could then use view 0 in your original example and it would work as expected.

void bgfx::setViewMode(ViewId _id, ViewMode::Enum _mode = ViewMode:: Default)

also, in the default mode maybe you were using bgfx::setViewScissor instead of:

uint16_t setScissor(uint16_t _x, uint16_t _y, uint16_t _width, uint16_t _height)

The difference is setScissor returns a cache id and sets scissor for the following draw primitive. To scissor for all primitives in view see bgfx::setViewScissor. Also once you have used setScissor if you saved the returned cache id you could reference the same scissor multiple times if needed by calling the overridden setScissor method.

void bgfx::setScissor(uint16_t _cache = UINT16_MAX)

When scissor rectangle is changing per draw call inside the same view use bgfx::setScissor, otherwise prefer bgfx::setViewScissor. I am thinking you are using bgfx::setViewScissor?
 

Ferdi

Member
3rd Party Target Dev
Joined
Jun 21, 2017
@AndroidAndy yes I am using bgfx::setViewScissor, but I am doing it to show the problem. I thought it was the easiest example for everyone to understand the problem. I am no bgfx expert, infact the start date of this thread is when I started learning bgfx :) I think I understand Mojo 2 now, except may be for renderer.cxs. So if you understand bgfx may be I can pick your brain.

The real problem is this, in Mojo 2 example bumptest.cxs in OnCreate method:

Code:
        'generate color texture
        Local colortex:=New Texture( 256,256,4,Texture.ClampST|Texture.RenderTarget )
        Local rcanvas:=New Canvas( colortex )
        rcanvas.Clear( 1,1,1 )
        rcanvas.Flush

        'generate normal texture  
        Local normtex:=New Texture( 256,256,4,Texture.ClampST|Texture.RenderTarget )
        rcanvas.SetRenderTarget( normtex )
        rcanvas.Clear( .5,.5,1.0,0.0 )
        For Local x:=0 Until 256 'Step 32
            For Local y:=0 Until 256 'Step 32
          
                Local dx:=x-127.5
                Local dy:=y-127.5
                Local dz:=127.5*127.5-dx*dx-dy*dy
          
                If dz<=0 Continue
          
                dz=Sqrt( dz )
          
                Local r:=(dx+127.5)/255.0
                Local g:=(dy+127.5)/-255.0
                Local b:=(dz+127.5)/255.0
          
                rcanvas.SetColor( r,g,b,1 )
                rcanvas.DrawPoint( x,y )

            Next
        Next
        rcanvas.Flush
Notice rcanvas is used to draw on 2 textures. So when SetRenderTarget is called, bgfx does the following: (sorry the code is simplified, hopefully you get what I mean)

Code:
bgfxSetViewFrameBuffer( 0, colortex )
' draw the color texture
...
bgfxSetViewFrameBuffer( 0, normtex )
' draw the normal texture
But when I tried bgfxSetViewMode( 0, BGFX_VIEW_MODE_SEQUENTIAL ), it doesn't work.

The only way I can get the above example to work is by changing the view id like this:

Code:
bgfxSetViewFrameBuffer( 0, colortex )
' draw the color texture
...
bgfxSetViewFrameBuffer( 1, normtex )
' draw the normal texture
Do I need to do something else to get BGFX_VIEW_MODE_SEQUENTIAL to work? Do you know?

EDIT: Do you want my Mojo 2 code? I also need to give you the bgfx modules + bgfx glfw3 target. The code is really messy, you have been warned.
 
Last edited:

AndyAndroid

Member
Joined
Jun 28, 2017
@Ferdi - I don't know bgfx, was just looking at the api docs to try and add to the discussion. The things I would double check is to make sure the Enum value you are passing is the correct value for sequential and is a struct bgfx::ViewMode. Then I would make sure that the call that the call to bgfx::setViewMode is set prior calling bgfx::submit for the view. Finally, since you have things working with two views then maybe try calling bgfx::setViewMode for both 0 and 1.

Even if you can't change the default view mode, it seems like at least in the example you showed, using bgfx::setScissor would give you the desired results as it should affect the next draw primitive? I am sure that I have oversimplified things, and only chiming in here to maybe help you see something you might have overlooked. Getting your Mojo 2 code right now would not work right now as I am so far behind your progress. I do like what you are doing and bgfx seems to be a great way to move forward. Thanks for posting your progress and issues here.
 

Ferdi

Member
3rd Party Target Dev
Joined
Jun 21, 2017
@Ferdi ... and only chiming in here to maybe help you see something you might have overlooked.
Please chime in, it is nice to have a second person looking at it. For Mojo 1 I have put your SetScissor fix in and I also put bgfxSetViewMode to BGFX_VIEW_MODE_SEQUENTIAL. And I have created a wrapper for Mojo 1. So instead writing "Import mojo", you need to write "Import mojo1bgfx".

My mojo 1 breakout and snake example game seems to be working. Devolonter/matchup is working, but gerryq/picpuzzle is not working (white screen). I managed to get mojo 1 mak/bouncyaliens going, and there doesn't seems to be a speed decrease using BGFX_VIEW_MODE_SEQUENTIAL. I do worry about mak dynamicimage (it has readpixels and writepixels), not sure if that can work. Also skn3/monkeystien is white screen. I am getting mixed results, I need to root cause the problem - why I am getting white screen on some examples.

The other thing that I found strange was bgfxSetViewClear. Clear usually clear the screen, but bgfxSetViewClear does NOT clear the screen. I have to do DrawRect to clear the screen. May be I am missing something here?
 
Top Bottom