Skip to content

Optimized mask.from_surface() when converting an alpha surface. #2895

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

itzpr3d4t0r
Copy link
Member

@itzpr3d4t0r itzpr3d4t0r commented May 31, 2024

This PR massively speeds up mask.from_surface() when dealing with the threshold argument (for 24 and 32 bit surfaces, about a 12% improvement for 16 and lower).

It achieves so by directly accessing the pixel's alpha channels instead of using SDL's functionality which is not only slow but actually did more work that we actually need for this operation by also returning the rgb channels.

With this new strategy we just jump from pixel's alpha to pixel's alpha and check that against the threshold.

I've also added a fast path for 24-bit surfaces bringing an astonishing 737 times improved runtime since we now use bitmask_fill.

I've also added a validity check for the threshold argument which wasn't there before.

here are the results:
image

made with this test program:

from data_utils import plot_tests, run_tests
import pygame
from pygame.mask import from_surface

pygame.init()

# ===========| CONFIG |===========

DO_TEST = 0
MAX_SIZE = 1000
REPETITIONS = 100
NUM_CALLS = 1

TITLE = "from_surface"
X_LABEL = "Surface size"
DO_SCATTER = True
MODE = "MIN"
LIMIT_TO_RANGE = MAX_SIZE
COMPARE_LIST = [(-1, 0)]
kwargs_dict = {"from_surface": from_surface}

def test_setup(curr_size: int, g: dict):
    s = pygame.Surface((curr_size, curr_size), pygame.SRCALPHA, 32)
    pygame.draw.circle(s, (255, 255, 255, 128), (curr_size // 2, curr_size // 2), curr_size // 2)
    g["s"] = s

tests = [
    ("mask from_surface new", "from_surface(s)"),
]

files = [
    ("mask from_surface new", "red"),
    ("mask from_surface old", "white")
]

if DO_TEST:
    run_tests(tests, test_setup, MAX_SIZE, REPETITIONS, NUM_CALLS, **kwargs_dict)

pygame.quit()

plot_tests(TITLE, files, MODE, LIMIT_TO_RANGE, DO_SCATTER, False, COMPARE_LIST,
           X_LABEL)

The data utils file (as txt):
data_utils.txt

@itzpr3d4t0r itzpr3d4t0r added Performance Related to the speed or resource usage of the project mask pygame.mask labels May 31, 2024
@itzpr3d4t0r itzpr3d4t0r requested a review from a team as a code owner May 31, 2024 09:17
Copy link
Member

@andrewhong04 andrewhong04 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The work you do never ceases to amaze me. This works on my machine just fine.

damusss
damusss previously approved these changes May 31, 2024
Copy link
Member

@damusss damusss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:pg_chad:

@damusss damusss dismissed their stale review June 14, 2024 16:29

It had no effect when I posted it and I didn't test it locally to be sure it worked, even tho I trust that it does

Copy link
Member

@damusss damusss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested locally with different sizes and I get a 65+% improvement for 32 bit surfaces and 10% with 16 bit surfaces, which is really impressive, good job!

Copy link
Member

@ankith26 ankith26 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also thinking more about it, I realised this probably breaks support for the more obscure kind of pixel formats (like say, SDL_PIXELFORMAT_ARGB2101010 or FOURCC style formats like YUV) but the pygame codebase probably doesn't support these in other places to begin with, so we can let that slide for now

itzpr3d4t0r and others added 2 commits July 8, 2024 13:55
Co-authored-by: Ankith <itsankith26@gmail.com>
Co-authored-by: Ankith <itsankith26@gmail.com>
Copy link
Member

@ankith26 ankith26 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay I am now satisfied that this PR should work for the common/normal cases.

Thanks for contributing! 🥳

@ankith26 ankith26 merged commit dbc6622 into pygame-community:main Jul 8, 2024
39 checks passed
@ankith26 ankith26 added this to the 2.5.1 milestone Jul 8, 2024
gresm pushed a commit to gresm/pygame-ce that referenced this pull request Jul 16, 2024
…game-community#2895)

* Optimized mask.from_surface when converting an alpha surface.

* Fix issues wit 16-bit surfaces, tests for invalid threshold values

* revert checks for threshold value

* add comment and small refactor

* fix

* make fast path more generic

* add early exit for threshold > 255

* Update src_c/mask.c

Co-authored-by: Ankith <itsankith26@gmail.com>

* Update src_c/mask.c

Co-authored-by: Ankith <itsankith26@gmail.com>

---------

Co-authored-by: Ankith <itsankith26@gmail.com>
@itzpr3d4t0r itzpr3d4t0r deleted the mask.from_surface_alpha-optimization branch August 7, 2024 09:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
mask pygame.mask Performance Related to the speed or resource usage of the project
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants