Implement "Shared Context Mutex" functionality.

Existing implementation uses single `GlobalMutex` for
- EGL calls
- GL calls for Contexts with concurrent access.

This CL introduces abstract `egl::ContextMutex` with two
implementations:
- SingleContextMutex;
- SharedContextMutex<Mutex>;

Note:
`std::mutex` is used in this commit. It is very easy to change mutex
type either at compile-time or at run-time (single type per Display).

When Context:
- is not Shared;
- does not use `EGLImage`s;
- does not use EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE
- does not use EGL_DISPLAY_SEMAPHORE_SHARE_GROUP_ANGLE
then it will be using `SingleContextMutex` with minimal overhead.

Before such Context is used as `shareContext` or uses `EGLImage`
its mutex replaced by `SharedContextMutex<Mutex>`.

The `GlobalMutex` is only used for EGL calls, while `egl::ContextMutex`
implementations for GL calls. Because some EGL calls use Context,
explicit `egl::ContextMutex` lock is required. This is implemented by
generating "egl_context_mutex_autogen.h" header, and insertion of
`ANGLE_EGL_SCOPED_CONTEXT_LOCK()` macro before `ANGLE_EGL_VALIDATE()`
in each EGL entry point. Implementation in "egl_context_lock_impl.h"
returns lock for required APIs. Special cases of `egl::ContextMutex`
lock handled separately. `std::unique_lock<>` is not used for
performance reasons.

`egl::ContextMutex` explicitly locked when capturing EGL calls.

Fixes EGLImage problem:
    e18240d136
    Mark contexts as shared when importing EGL images.

Details:
- EGLImage inherits Context's mutex when created.
  Mutex is used when the EGLImage accessed or destroyed.
- When EGLImage is used in Context with other `egl::ContextMutex`,
  two mutexes are merged into one.
- After the mutex merge, Context Groups will remain separate,
  but will not be able to run in parallel.

Fixes race when checking `context->isShared()` in the
`SCOPED_SHARE_CONTEXT_LOCK()` macro. One Context may start executing GL
call while not "Shared", but become "Shared" inside the call. New
(second) "Shared" Context may immediately start using GL and potentially
corrupt some "Shared" state.

Possible performance benefit: allows parallel execution in some cases,
when single `GlobalMutex` would block.

Important note:
    Process of replacing the `SingleContextMutex` by
    `SharedContextMutex<Mutex>` is not 100% safe. This mean that
    original Context may still be using `SingleContextMutex` after
    activating `SharedContextMutex<Mutex>`. However, this was always
    the case before introduction of this CL. Old `Context::mShared`
    member update was not synchronized in any way at all. In other
    words, this solution does not 100% fix the original problem.
    For 100% safe solution `SingleContextMutex` should not be used
    (always pass `SharedContextMutex<Mutex>` to the `gl::Context`
    constructor). See `lockAndActivateSharedContextMutex()` for more
    details.

CL adds new build option:
    angle_enable_shared_context_mutex = true

Behavior with other build options:
- When:
    `angle_enable_shared_context_mutex`    is disabled or
    `angle_enable_share_context_lock`      is disabled or
    `angle_force_context_check_every_call` is enabled,
  Contexts will always have `SingleContextMutex`, however it will be
  only used in special cases. `SCOPED_SHARE_CONTEXT_LOCK()` will use
  `GlobalMutex` when applicable.
- Otherwise, `SCOPED_SHARE_CONTEXT_LOCK()` will use `egl::ContextMutex`.

Some GFXBench "1080p Driver Overhead 2 Offscreen" performance numbers.
Tested on S906B (Samsung Galaxy S22+) on old ANGLE base:
    807c94ea85
    Capture/Replay: Adjust tests do adhere to capture limits

Each test result is an average frame number from 6 runs.

    SingleContextMutex                            6579 ( +0.13%)
    (old) GetContextLock() (mShared is false)     6570

Forced `mShared = true` or NOT using `SingleContextMutex`.

    SharedContextMutex<std::mutex> FORCE          5061 (-22.97%)
    (old) GetContextLock() FORCE                  4766 (-27.46%)

Bug: angleproject:6957
Bug: chromium:1336126
Change-Id: Idcd919f9d4bf482b9ae489bd8b4415ec96048e32
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4374545
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
This commit is contained in:
Igor Nazarov
2023-01-17 17:42:59 +02:00
committed by Angle LUCI CQ
parent 82151df0c9
commit 36c3e0f546
26 changed files with 2931 additions and 380 deletions

View File

@@ -6,7 +6,7 @@
"scripts/entry_point_packed_gl_enums.json":
"1c6b036918aabb9822a638fbf33f87f4",
"scripts/generate_entry_points.py":
"af2e7b9911842300f7cbdd3230e279cb",
"b85e2df1673ba499b02d520623ff2637",
"scripts/gl_angle_ext.xml":
"49a0bf469d6f44c532098ef3a9fd087f",
"scripts/registry_xml.py":
@@ -113,6 +113,8 @@
"f021ca1ad124333ef618080890f13582",
"src/libGLESv2/cl_stubs_autogen.h":
"6d880c6b65284192b5842f0e42ad2741",
"src/libGLESv2/egl_context_lock_autogen.h":
"4cf7d5d5e35ca438de4104ac4ceb2b89",
"src/libGLESv2/egl_ext_stubs_autogen.h":
"1826fdc1676e19ebf86a20ec76ea51c0",
"src/libGLESv2/egl_get_labeled_object_data.json":
@@ -124,11 +126,11 @@
"src/libGLESv2/entry_points_cl_autogen.h":
"dde2f94c3004874a7da995dae69da811",
"src/libGLESv2/entry_points_egl_autogen.cpp":
"dbb7a09d338d2fb0809abddd62b2578f",
"82dc17eb4990c965f98ec672b7ce9d94",
"src/libGLESv2/entry_points_egl_autogen.h":
"3bc7a8df9deadd7cfd615d0cfad0c6a8",
"src/libGLESv2/entry_points_egl_ext_autogen.cpp":
"bff96d039ef189414bec3451c8bb379b",
"d73defbc1b89b16f5de42849633d43ba",
"src/libGLESv2/entry_points_egl_ext_autogen.h":
"5a212372e378e0890d2d3ac96c1a3765",
"src/libGLESv2/entry_points_gl_1_autogen.cpp":
@@ -168,7 +170,7 @@
"src/libGLESv2/entry_points_gles_3_2_autogen.h":
"647f932a299cdb4726b60bbba059f0d2",
"src/libGLESv2/entry_points_gles_ext_autogen.cpp":
"827e15c2f31276f9207f9687a4bc4c7a",
"4fb06f0ab32147310686fedf1bc116bf",
"src/libGLESv2/entry_points_gles_ext_autogen.h":
"7bb44566362d1de21552faf427517085",
"src/libGLESv2/libGLESv2_autogen.cpp":

View File

@@ -306,9 +306,13 @@ void EGLAPIENTRY EGL_{name}({params})
{packed_gl_enum_conversions}
ANGLE_EGL_VALIDATE_VOID(thread, {name}, {labeled_object}, {internal_params});
{{
ANGLE_EGL_SCOPED_CONTEXT_LOCK({name}, thread{comma_if_needed_context_lock}{internal_context_lock_params});
ANGLE_EGL_VALIDATE_VOID(thread, {name}, {labeled_object}, {internal_params});
{name}(thread{comma_if_needed}{internal_params});
}}
{name}(thread{comma_if_needed}{internal_params});
ANGLE_CAPTURE_EGL({name}, true, {egl_capture_params});
}}
{epilog}
@@ -327,9 +331,13 @@ TEMPLATE_EGL_ENTRY_POINT_WITH_RETURN = """\
{packed_gl_enum_conversions}
ANGLE_EGL_VALIDATE(thread, {name}, {labeled_object}, {return_type}{comma_if_needed}{internal_params});
{{
ANGLE_EGL_SCOPED_CONTEXT_LOCK({name}, thread{comma_if_needed_context_lock}{internal_context_lock_params});
ANGLE_EGL_VALIDATE(thread, {name}, {labeled_object}, {return_type}{comma_if_needed}{internal_params});
returnValue = {name}(thread{comma_if_needed}{internal_params});
}}
returnValue = {name}(thread{comma_if_needed}{internal_params});
ANGLE_CAPTURE_EGL({name}, true, {egl_capture_params}, returnValue);
}}
{epilog}
@@ -588,6 +596,30 @@ namespace egl
#endif // LIBANGLE_VALIDATION_{annotation}_AUTOGEN_H_
"""
TEMPLATE_EGL_CONTEXT_LOCK_HEADER = """\
// GENERATED FILE - DO NOT EDIT.
// Generated by {script_name} using data from {data_source_name}.
//
// Copyright 2023 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// {annotation_lower}_context_lock_autogen.h:
// Context Lock functions for the {comment} entry points.
#ifndef LIBGLESV2_{annotation_upper}_CONTEXT_LOCK_AUTOGEN_H_
#define LIBGLESV2_{annotation_upper}_CONTEXT_LOCK_AUTOGEN_H_
#include "libGLESv2/global_state.h"
namespace egl
{{
{prototypes}
}} // namespace egl
#endif // LIBGLESV2_{annotation_upper}_CONTEXT_LOCK_AUTOGEN_H_
"""
TEMPLATE_CAPTURE_HEADER = """\
// GENERATED FILE - DO NOT EDIT.
// Generated by {script_name} using data from {data_source_name}.
@@ -955,6 +987,7 @@ EGL_SOURCE_INCLUDES = """\
#include "libANGLE/capture/capture_egl_autogen.h"
#include "libANGLE/entry_points_utils.h"
#include "libANGLE/validationEGL_autogen.h"
#include "libGLESv2/egl_context_lock_impl.h"
#include "libGLESv2/egl_stubs_autogen.h"
#include "libGLESv2/egl_ext_stubs_autogen.h"
#include "libGLESv2/global_state.h"
@@ -974,6 +1007,7 @@ EGL_EXT_SOURCE_INCLUDES = """\
#include "libANGLE/capture/capture_egl_autogen.h"
#include "libANGLE/entry_points_utils.h"
#include "libANGLE/validationEGL_autogen.h"
#include "libGLESv2/egl_context_lock_impl.h"
#include "libGLESv2/egl_ext_stubs_autogen.h"
#include "libGLESv2/global_state.h"
@@ -1074,6 +1108,8 @@ TEMPLATE_CAPTURE_PROTO = "angle::CallCapture Capture%s(%s);"
TEMPLATE_VALIDATION_PROTO = "%s Validate%s(%s);"
TEMPLATE_CONTEXT_LOCK_PROTO = "ScopedContextMutexLock GetContextLock_%s(%s);"
TEMPLATE_WINDOWS_DEF_FILE = """\
; GENERATED FILE - DO NOT EDIT.
; Generated by {script_name} using data from {data_source_name}.
@@ -1618,6 +1654,12 @@ def format_entry_point_def(api, command_node, cmd_name, proto, params, cmd_packe
else:
has_errcode_ret = False
internal_context_lock_params = [
just_the_name_packed(param, packed_enums) for param in params if just_the_type_packed(
param, packed_enums) in ["Thread *", "egl::Display *", "gl::ContextID"] or
just_the_name_packed(param, packed_enums) in ["attribute"]
]
packed_gl_enum_conversions = []
for param in params:
@@ -1650,6 +1692,8 @@ def format_entry_point_def(api, command_node, cmd_name, proto, params, cmd_packe
", ".join(params),
"internal_params":
", ".join(internal_params),
"internal_context_lock_params":
", ".join(internal_context_lock_params),
"initialization":
initialization,
"packed_gl_enum_conversions":
@@ -1658,6 +1702,8 @@ def format_entry_point_def(api, command_node, cmd_name, proto, params, cmd_packe
", ".join(pass_params),
"comma_if_needed":
", " if len(params) > 0 else "",
"comma_if_needed_context_lock":
", " if len(internal_context_lock_params) > 0 else "",
"gl_capture_params":
", ".join(["context"] + internal_params),
"egl_capture_params":
@@ -1828,6 +1874,20 @@ def get_validation_params(api, cmd_name, params, cmd_packed_gl_enums, packed_par
])
def get_context_lock_params(api, cmd_name, params, cmd_packed_gl_enums, packed_param_types):
packed_gl_enums = get_packed_enums(api, cmd_packed_gl_enums, cmd_name, packed_param_types,
params)
return ", ".join([
make_param(
just_the_type_packed(param, packed_gl_enums),
just_the_name_packed(param, packed_gl_enums))
for param in params
if just_the_type_packed(
param, packed_gl_enums) in ["Thread *", "egl::Display *", "gl::ContextID"] or
just_the_name_packed(param, packed_gl_enums) in ["attribute"]
])
def format_context_decl(api, cmd_name, proto, params, template, cmd_packed_gl_enums,
packed_param_types):
internal_params = get_internal_params(api, cmd_name, params, cmd_packed_gl_enums,
@@ -1873,6 +1933,13 @@ def format_validation_proto(api, cmd_name, proto, params, cmd_packed_gl_enums, p
return TEMPLATE_VALIDATION_PROTO % (return_type, strip_api_prefix(cmd_name), internal_params)
def format_context_lock_proto(api, cmd_name, params, cmd_packed_gl_enums, packed_param_types):
with_extra_params = ["Thread *thread"] + params
internal_params = get_context_lock_params(api, cmd_name, with_extra_params,
cmd_packed_gl_enums, packed_param_types)
return TEMPLATE_CONTEXT_LOCK_PROTO % (strip_api_prefix(cmd_name), internal_params)
def format_capture_proto(api, cmd_name, proto, params, cmd_packed_gl_enums, packed_param_types):
context_param_typed = 'egl::Thread *thread' if api == apis.EGL else 'const State &glState'
internal_params = get_internal_params(api, cmd_name,
@@ -1905,6 +1972,7 @@ class ANGLEEntryPoints(registry_xml.EntryPoints):
self.defs = []
self.export_defs = []
self.validation_protos = []
self.context_lock_protos = []
self.capture_protos = []
self.capture_methods = []
self.capture_pointer_funcs = []
@@ -1922,6 +1990,11 @@ class ANGLEEntryPoints(registry_xml.EntryPoints):
format_validation_proto(self.api, cmd_name, proto_text, param_text,
cmd_packed_enums, packed_param_types))
if api == apis.EGL:
self.context_lock_protos.append(
format_context_lock_proto(api, cmd_name, param_text, cmd_packed_enums,
packed_param_types))
self.capture_protos.append(
format_capture_proto(self.api, cmd_name, proto_text, param_text, cmd_packed_enums,
packed_param_types))
@@ -2196,6 +2269,22 @@ def write_validation_header(annotation, comment, protos, source, template):
out.close()
def write_context_lock_header(annotation, comment, protos, source, template):
content = template.format(
script_name=os.path.basename(sys.argv[0]),
data_source_name=source,
annotation_lower=annotation.lower(),
annotation_upper=annotation.upper(),
comment=comment,
prototypes="\n".join(protos))
path = path_to("libGLESv2", "%s_context_lock_autogen.h" % annotation.lower())
with open(path, "w") as out:
out.write(content)
out.close()
def write_gl_validation_header(annotation, comment, protos, source):
return write_validation_header(annotation, comment, protos, source,
TEMPLATE_GL_VALIDATION_HEADER)
@@ -2661,8 +2750,9 @@ def get_egl_entry_point_labeled_object(ep_to_object, cmd_stripped, params, packe
def get_context_lock(api, cmd_name):
# EGLImage related commands need to access EGLImage and Display which should
# be protected with global lock
# Also handles ContexMutex marging when SharedContextMutex is enabled.
if api == apis.GLES and cmd_name.startswith("glEGLImage"):
return "SCOPED_GLOBAL_AND_SHARE_CONTEXT_LOCK(context);"
return "SCOPED_EGL_IMAGE_SHARE_CONTEXT_LOCK(context, imagePacked);"
return "SCOPED_SHARE_CONTEXT_LOCK(context);"
@@ -2855,6 +2945,7 @@ def main():
'../src/libGLESv2/entry_points_gl_3_autogen.h',
'../src/libGLESv2/entry_points_gl_4_autogen.cpp',
'../src/libGLESv2/entry_points_gl_4_autogen.h',
'../src/libGLESv2/egl_context_lock_autogen.h',
'../util/capture/frame_capture_replay_autogen.cpp',
]
@@ -3197,6 +3288,7 @@ def main():
eglxml = registry_xml.RegistryXML('egl.xml', 'egl_angle_ext.xml')
egl_validation_protos = []
egl_context_lock_protos = []
egl_decls = ["extern \"C\" {"]
egl_defs = ["extern \"C\" {"]
libegl_ep_defs = []
@@ -3231,6 +3323,7 @@ def main():
egl_defs += [comment] + eps.defs
libegl_ep_defs += [comment] + eps.export_defs
egl_validation_protos += [comment] + eps.validation_protos
egl_context_lock_protos += [comment] + eps.context_lock_protos
libegl_windows_def_exports += [win_def_comment] + get_exports(eglxml.commands[version])
egl_capture_protos += eps.capture_protos
egl_capture_methods += eps.capture_methods
@@ -3268,6 +3361,7 @@ def main():
egl_ext_defs += [comment] + eps.defs
libegl_ep_defs += [comment] + eps.export_defs
egl_validation_protos += [comment] + eps.validation_protos
egl_context_lock_protos += [comment] + eps.context_lock_protos
libegl_windows_def_exports += [win_def_comment] + get_exports(ext_cmd_names)
egl_capture_protos += eps.capture_protos
egl_capture_methods += eps.capture_methods
@@ -3286,6 +3380,8 @@ def main():
"cpp", EGL_EXT_SOURCE_INCLUDES, "libGLESv2", "egl.xml and egl_angle_ext.xml")
write_validation_header("EGL", "EGL", egl_validation_protos, "egl.xml and egl_angle_ext.xml",
TEMPLATE_EGL_VALIDATION_HEADER)
write_context_lock_header("EGL", "EGL", egl_context_lock_protos,
"egl.xml and egl_angle_ext.xml", TEMPLATE_EGL_CONTEXT_LOCK_HEADER)
write_stubs_header("EGL", "egl_ext", "EXT extension", "egl.xml and egl_angle_ext.xml",
EGL_EXT_STUBS_HEADER_PATH, eglxml.all_commands, egl_ext_commands,
EGLEntryPoints.get_packed_enums(), EGL_PACKED_TYPES)