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.