Agreed! Nice find! 

The default Window config code can be found in pyglet/window/__init__.py, 
starting from line 511. It simply tries a few configs, starting with 24bit. 
Looking at your results, I don't see any reason why we can't switch this 
around to try a 16bit buffer first. This is only for the default, so anyone 
with a specific need can easily create their own Config. 
We're defaulting to orthographic rendering anyway!

....

if not config:
    for template_config in [
        gl.Config(double_buffer=True, depth_size=24),
        gl.Config(double_buffer=True, depth_size=16),
        None]:
        try:
            config = screen.get_best_config(template_config)
            break
        except NoSuchConfigException:
            pass
    if not config:
        raise NoSuchConfigException('No standard config is available.')

...

On Saturday, May 26, 2018 at 4:34:25 PM UTC+9, Richard Jones wrote:
>
> That is a great finding, thanks for delving into it and sharing!!
>
> On 26 May 2018 at 17:01, Daniel Gillet <[email protected] <javascript:>> 
> wrote:
>
>> Hello,
>>
>> I recently was trying some code and I found something which was both 
>> surprising and interesting. I thought I would share my findings with you.
>>
>> I wanted to play a bit with moderngl 
>> <https://github.com/cprogrammer1994/ModernGL>, an OpenGL binding. I 
>> started initially by using pygame to create the window and handle the 
>> events. My goal was just to use OpenGL 3 features to see how I could 
>> display lots of moving and rotating sprites on the screen. I also used 
>> numpy for storing my arrays and I managed to get 10,000 moving and rotating 
>> quads on my screen while barely maintaining 60 FPS.
>>
>> Then I thought I would try to port that code to pyglet, but still using 
>> moderngl. And this is where the situation gets interesting. After finishing 
>> the porting, my performances dropped to 30 FPS. The interesting part was 
>> that I was not using any OpenGL calls from either pygame (doesn't have any 
>> anyways) nor pyglet. So I knew the OpenGL stuff was not responsible for 
>> this drop in performances. I started to profile both examples and both 
>> showed that the bottleneck was the flip function. This makes sense as 
>> this is where all the data get loaded on the graphic card. But pygame flip 
>> function was taking something like 15 ms while pyglet flip function was 
>> taking twice as much, around 30 ms.
>>
>> I went down the rabbit hole and I'll spare you with the details of all 
>> the things I tried. I'll go straight to my findings. Pygame and Pyglet were 
>> not giving me the same OpenGL context. Pygame gave me an OpenGL with a 16 
>> bits depth buffer and no stencil buffer, while Pyglet was giving me a 24 
>> bits depth buffer together with an 8 bits stencil buffer. And obviously 
>> swapping extra information has a cost.
>>
>> I added the following code to my pyglet code:
>>
>> config = pyglet.gl.Config(double_buffer=True, depth_size=16)
>> window = pyglet.window.Window(
>>     width=1366, height=768, resizable=True, vsync=True, config=config
>> )
>>
>> And guess what? I had the same performances as with Pygame, reaching 
>> barely 60 FPS with 10,000 textured quads moving and rotating on the screen.
>>
>> In conclusion Pyglet offers by default an OpenGL context with better 
>> depth buffer and a stencil buffer. But if you don't use them, they come at 
>> a cost. I don't know how many Pyglet app really need the 24 bits depth 
>> buffer together with an 8 bits stencil buffer. Just by changing the OpenGL 
>> config, you might get a performance boost if you're hitting heavily the 
>> graphics card.
>>
>> For those interested in my little test code, here it is:
>>
>> import time
>> import collections
>>
>> import pyglet
>>
>> import moderngl
>> import numpy as np
>> from vec_utils import create_orthogonal_projection
>>
>> import cProfile, pstats
>>
>> pr = cProfile.Profile()
>> pr.enable()
>>
>>
>> class FPSCounter:
>>     def __init__(self):
>>         self.time = time.perf_counter()
>>         self.frame_times = collections.deque(maxlen=60)
>>
>>     def tick(self):
>>         t1 = time.perf_counter()
>>         dt = t1 - self.time
>>         self.time = t1
>>         self.frame_times.append(dt)
>>
>>     def get_fps(self):
>>         return len(self.frame_times) / sum(self.frame_times)
>>
>>
>> fps_counter = FPSCounter()
>> config = pyglet.gl.Config(double_buffer=True, depth_size=16)
>> window = pyglet.window.Window(
>>     width=1366, height=768, resizable=True, vsync=True, config=config
>> )
>>
>> ctx = moderngl.create_context()
>> ctx.viewport = (0, 0) + window.get_size()
>> prog = ctx.program(
>>     vertex_shader='''
>>         #version 330
>>         uniform mat4 Projection;
>>
>>         in vec2 in_vert;
>>         in vec2 in_texture;
>>
>>         in vec3 in_pos;
>>         in float in_angle;
>>         in vec2 in_scale;
>>
>>         out vec2 v_texture;
>>
>>         void main() {
>>             mat2 rotate = mat2(
>>                         cos(in_angle), sin(in_angle),
>>                         -sin(in_angle), cos(in_angle)
>>                     );
>>             vec3 pos;
>>             pos = in_pos + vec3(rotate * (in_vert * in_scale), 0.);
>>             gl_Position = Projection * vec4(pos, 1.0);
>>             v_texture = in_texture;
>>         }
>>     ''',
>>     fragment_shader='''
>>         #version 330
>>         uniform sampler2D Texture;
>>
>>         in vec2 v_texture;
>>
>>         out vec4 f_color;
>>
>>         void main() {
>>             vec4 basecolor = texture(Texture, v_texture);
>>
>>             if (basecolor.a == 0.0){
>>                 discard;
>>             }
>>             f_color = basecolor;
>>         }
>>     ''',
>> )
>> vertices = np.array([
>>     #  x,    y,   u,   v
>>     -1.0, -1.0, 0.0, 0.0,
>>     -1.0,  1.0, 0.0, 1.0,
>>      1.0, -1.0, 1.0, 0.0,
>>      1.0,  1.0, 1.0, 1.0,
>>     ], dtype=np.float32
>> )
>>
>> proj = create_orthogonal_projection(
>>     left=0, right=600, bottom=0, top=400, near=-1000, far=100, 
>> dtype=np.float32
>> )
>>
>> img = pyglet.image.load('grossinis2.png', 
>> file=pyglet.resource.file('grossinis2.png'))
>> texture = ctx.texture((img.width, img.height), 4, img.get_data("RGBA", 
>> img.pitch))
>> texture.use(0)
>>
>> INSTANCES = 10_000
>>
>> # pos_scale = np.array([
>> #       # pos_x, pos_y,  z, angle,   scale_x,       scale_y
>> #         100.0, 150.0, 0., 0., rect.width/2, rect.height/2,
>> #         120.5, 200.0, 10., 0., rect.width/2, rect.height/2,
>> #     ], dtype=np.float32)
>>
>> positions = (np.random.rand(INSTANCES, 3) * 1000).astype('f4')
>> angles = (np.random.rand(INSTANCES, 1) * 2 * np.pi).astype('f4')
>> sizes = np.tile(np.array([img.width / 2, img.height / 2], 
>> dtype=np.float32),
>>                 (INSTANCES, 1)
>>                 )
>> pos_scale = np.hstack((positions, angles, sizes))
>> player_pos = pos_scale[0, :]
>>
>> pos_scale_buf = ctx.buffer(pos_scale.tobytes())
>>
>>
>> vbo = ctx.buffer(vertices.tobytes())
>> vao_content = [
>>     (vbo, '2f 2f', 'in_vert', 'in_texture'),
>>     (pos_scale_buf, '3f 1f 2f/i', 'in_pos', 'in_angle', 'in_scale')
>> ]
>> vao = ctx.vertex_array(prog, vao_content)
>> ctx.enable(moderngl.BLEND)
>> ctx.enable(moderngl.DEPTH_TEST)
>>
>>
>> def show_fps(dt):
>>     print(f"FPS: {fps_counter.get_fps()}")
>>
>>
>> def update(dt):
>>     pos_scale[1:, 0] += 0.1
>>     pos_scale[1:, 3] += 0.01
>>     pos_scale[1::2, 2] += 0.1
>>
>>
>> def report(dt):
>>     pr.disable()
>>     with open("pyglet_stats.txt", "w") as f:
>>         sortby = 'tottime'
>>         ps = pstats.Stats(pr, stream=f).sort_stats(sortby)
>>         ps.print_stats()
>>     print("Report written")
>>
>>
>> pyglet.clock.schedule_once(report, 5)
>>
>> pyglet.clock.schedule_interval(show_fps, 1)
>> pyglet.clock.schedule_interval(update, 1 / 60)
>>
>>
>> @window.event
>> def on_resize(width, height):
>>     global proj
>>     ctx.viewport = (0, 0, width, height)
>>     proj = create_orthogonal_projection(
>>         left=0, right=width, bottom=0, top=height, near=-1000, far=100, 
>> dtype=np.float32
>>     )
>>     return True
>>
>>
>> @window.event
>> def on_draw():
>>     ctx.clear(0.0, 0.0, 0.0, 0.0, depth=1.0)
>>
>>     prog['Texture'].value = 0
>>     prog['Projection'].write(proj.tobytes())
>>     pos_scale_buf.write(pos_scale.tobytes())
>>     vao.render(moderngl.TRIANGLE_STRIP, instances=INSTANCES)
>>     pos_scale_buf.orphan()
>>     fps_counter.tick()
>>
>>
>> pyglet.app.run()
>>
>> And the vec_utils.py contains the create_orthogonal_projection function 
>> which is a straight copy paste from the Pyrr project.
>>
>> import numpy as np
>>
>> def create_orthogonal_projection(
>>     left,
>>     right,
>>     bottom,
>>     top,
>>     near,
>>     far,
>>     dtype=None
>> ):
>>     """Creates an orthogonal projection matrix.
>>     :param float left: The left of the near plane relative to the plane's 
>> centre.
>>     :param float right: The right of the near plane relative to the 
>> plane's centre.
>>     :param float top: The top of the near plane relative to the plane's 
>> centre.
>>     :param float bottom: The bottom of the near plane relative to the 
>> plane's centre.
>>     :param float near: The distance of the near plane from the camera's 
>> origin.
>>         It is recommended that the near plane is set to 1.0 or above to 
>> avoid rendering issues
>>         at close range.
>>     :param float far: The distance of the far plane from the camera's 
>> origin.
>>     :rtype: numpy.array
>>     :return: A projection matrix representing the specified orthogonal 
>> perspective.
>>     .. seealso:: 
>> http://msdn.microsoft.com/en-us/library/dd373965(v=vs.85).aspx
>>     """
>>
>>     """
>>     A 0 0 Tx
>>     0 B 0 Ty
>>     0 0 C Tz
>>     0 0 0 1
>>     A = 2 / (right - left)
>>     B = 2 / (top - bottom)
>>     C = -2 / (far - near)
>>     Tx = (right + left) / (right - left)
>>     Ty = (top + bottom) / (top - bottom)
>>     Tz = (far + near) / (far - near)
>>     """
>>     rml = right - left
>>     tmb = top - bottom
>>     fmn = far - near
>>
>>     A = 2. / rml
>>     B = 2. / tmb
>>     C = -2. / fmn
>>     Tx = -(right + left) / rml
>>     Ty = -(top + bottom) / tmb
>>     Tz = -(far + near) / fmn
>>
>>     return np.array((
>>         ( A, 0., 0., 0.),
>>         (0.,  B, 0., 0.),
>>         (0., 0.,  C, 0.),
>>         (Tx, Ty, Tz, 1.),
>>     ), dtype=dtype)
>>
>> Daniel
>>
>> -- 
>> You received this message because you are subscribed to the Google Groups 
>> "pyglet-users" group.
>> To unsubscribe from this group and stop receiving emails from it, send an 
>> email to [email protected] <javascript:>.
>> To post to this group, send email to [email protected] 
>> <javascript:>.
>> Visit this group at https://groups.google.com/group/pyglet-users.
>> For more options, visit https://groups.google.com/d/optout.
>>
>
>

-- 
You received this message because you are subscribed to the Google Groups 
"pyglet-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/pyglet-users.
For more options, visit https://groups.google.com/d/optout.

Reply via email to