One argument in favor of defaulting to 24 bit depth buffer instead of 16
bit depth buffer is that:

- some people understand all this and can optimize the settings for their
application;

- but for people who don't understand it all, or who don't delve into the
details, the kind of bugs they'll get from a lower resolution depth buffer
(namely, z-fighting or the wrong polygon being in front) might seem
mysterious to them (even assuming they learned enough OpenGL to write or
use code to generate 3d polygons), and it might never occur to them that
they should try increasing their depth buffer resolution -- they might not
even know it's possible, or they might assume pyglet already gave them the
best (for accuracy, not speed) result it could.

(It's also possible that if they manually increase it, their app will now
fail on some platforms, whereas the default code you mentioned
automatically downgrades instead of failing -- I don't know.)

In other words, it seems to me like pyglet is now optimizing for "just
working out of the box, and giving the most accurate results", rather than
"giving the best possible speed"; and it seems to me this is the right
choice for pyglet's target audience, which I see as "people who want to
learn just enough OpenGL to do something interesting" rather than "people
who want to write a high-performance app and don't mind becoming experts in
order to do that" (the latter will probably realize they could get better
speed by moving to something other than pyglet).

(BTW, I do also appreciate the OP's finding -- it's great to know how to
speed things up when you do want to (after getting them working).)

- Bruce Smith

On Sat, May 26, 2018 at 6:54 AM, Benjamin Moran <[email protected]>
wrote:

> 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]> 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('gro
>>> ssinis2.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-u
>>> s/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].
>>> 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.
>>>
>>
>> --
> 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.
>

-- 
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