Commit 1d7046ca by John Plate Committed by Commit Bot

Generate CL stubs in libGLESv2

Bug: angleproject:5758 Change-Id: I6440dacf0db57a56923d2cab5a7c791981ba3b9d Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2822248 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCody Northrop <cnorthrop@google.com>
parent f7891766
//
// Copyright 2021 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.
//
// angle_cl.h:
// Includes all necessary CL headers and definitions for ANGLE.
//
#ifndef ANGLECL_H_
#define ANGLECL_H_
#define CL_TARGET_OPENCL_VERSION 300
#define CL_USE_DEPRECATED_OPENCL_1_0_APIS
#define CL_USE_DEPRECATED_OPENCL_1_1_APIS
#define CL_USE_DEPRECATED_OPENCL_1_2_APIS
#define CL_USE_DEPRECATED_OPENCL_2_0_APIS
#define CL_USE_DEPRECATED_OPENCL_2_1_APIS
#define CL_USE_DEPRECATED_OPENCL_2_2_APIS
#include "CL/opencl.h"
#endif // ANGLECL_H_
...@@ -3517,7 +3517,6 @@ server's OpenCL/api-docs repository. ...@@ -3517,7 +3517,6 @@ server's OpenCL/api-docs repository.
</command> </command>
<command prefix="CL_EXT_PREFIX__VERSION_1_1_DEPRECATED" suffix="CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED"> <command prefix="CL_EXT_PREFIX__VERSION_1_1_DEPRECATED" suffix="CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED">
<proto><type>cl_int</type> <name>clUnloadCompiler</name></proto> <proto><type>cl_int</type> <name>clUnloadCompiler</name></proto>
<param><type>void</type> </param>
</command> </command>
<command prefix="CL_EXT_PREFIX__VERSION_1_1_DEPRECATED" suffix="CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED"> <command prefix="CL_EXT_PREFIX__VERSION_1_1_DEPRECATED" suffix="CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED">
<proto><type>void</type>* <name>clGetExtensionFunctionAddress</name></proto> <proto><type>void</type>* <name>clGetExtensionFunctionAddress</name></proto>
......
{ {
"scripts/cl.xml": "scripts/cl.xml":
"c7b7bf8184d73c991f3a1ca11e882894", "f923201d4ea3e1130763b19fa7faa7a2",
"scripts/egl.xml": "scripts/egl.xml":
"013c552e6c523abdcf268268ea47e9fe", "013c552e6c523abdcf268268ea47e9fe",
"scripts/egl_angle_ext.xml": "scripts/egl_angle_ext.xml":
......
{ {
"scripts/cl.xml": "scripts/cl.xml":
"c7b7bf8184d73c991f3a1ca11e882894", "f923201d4ea3e1130763b19fa7faa7a2",
"scripts/egl.xml": "scripts/egl.xml":
"013c552e6c523abdcf268268ea47e9fe", "013c552e6c523abdcf268268ea47e9fe",
"scripts/egl_angle_ext.xml": "scripts/egl_angle_ext.xml":
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
"scripts/entry_point_packed_gl_enums.json": "scripts/entry_point_packed_gl_enums.json":
"4f7b43863a5e61991bba4010db463679", "4f7b43863a5e61991bba4010db463679",
"scripts/generate_entry_points.py": "scripts/generate_entry_points.py":
"5b0afb91add7f1cffd526d5df07aa7e7", "cddb06dad6ee60412d7b7b149966e2fd",
"scripts/gl.xml": "scripts/gl.xml":
"2a73a58a7e26d8676a2c0af6d528cae6", "2a73a58a7e26d8676a2c0af6d528cae6",
"scripts/gl_angle_ext.xml": "scripts/gl_angle_ext.xml":
...@@ -119,6 +119,8 @@ ...@@ -119,6 +119,8 @@
"acd2e1984277482df9cc937a83a5df05", "acd2e1984277482df9cc937a83a5df05",
"src/libGL/libGL_autogen.def": "src/libGL/libGL_autogen.def":
"2789d87b05eea9f53d52e2aff499b785", "2789d87b05eea9f53d52e2aff499b785",
"src/libGLESv2/cl_stubs_autogen.h":
"088690549f9eab4aab860d146d4a9c92",
"src/libGLESv2/egl_ext_stubs_autogen.h": "src/libGLESv2/egl_ext_stubs_autogen.h":
"2ef3b8d087f2a97f7270b96077c93856", "2ef3b8d087f2a97f7270b96077c93856",
"src/libGLESv2/egl_get_labeled_object_data.json": "src/libGLESv2/egl_get_labeled_object_data.json":
...@@ -168,7 +170,7 @@ ...@@ -168,7 +170,7 @@
"src/libOpenCL/entry_points_cl_autogen.cpp": "src/libOpenCL/entry_points_cl_autogen.cpp":
"36dc767bd0a475f2ca58549516cf59b1", "36dc767bd0a475f2ca58549516cf59b1",
"src/libOpenCL/entry_points_cl_autogen.h": "src/libOpenCL/entry_points_cl_autogen.h":
"ef47b0f6110f4a7ef59322c6aad763ab", "2e454a6373c1d0964490797898fb630f",
"src/libOpenCL/libOpenCL_autogen.cpp": "src/libOpenCL/libOpenCL_autogen.cpp":
"84da77a2f0a740f3a547e24fb1f40208" "84da77a2f0a740f3a547e24fb1f40208"
} }
\ No newline at end of file
{ {
"scripts/cl.xml": "scripts/cl.xml":
"c7b7bf8184d73c991f3a1ca11e882894", "f923201d4ea3e1130763b19fa7faa7a2",
"scripts/egl.xml": "scripts/egl.xml":
"013c552e6c523abdcf268268ea47e9fe", "013c552e6c523abdcf268268ea47e9fe",
"scripts/egl_angle_ext.xml": "scripts/egl_angle_ext.xml":
......
...@@ -13,6 +13,7 @@ import registry_xml ...@@ -13,6 +13,7 @@ import registry_xml
from registry_xml import apis, script_relative, strip_api_prefix from registry_xml import apis, script_relative, strip_api_prefix
# Paths # Paths
CL_STUBS_HEADER_PATH = "../src/libGLESv2/cl_stubs_autogen.h"
EGL_GET_LABELED_OBJECT_DATA_PATH = "../src/libGLESv2/egl_get_labeled_object_data.json" EGL_GET_LABELED_OBJECT_DATA_PATH = "../src/libGLESv2/egl_get_labeled_object_data.json"
EGL_STUBS_HEADER_PATH = "../src/libGLESv2/egl_stubs_autogen.h" EGL_STUBS_HEADER_PATH = "../src/libGLESv2/egl_stubs_autogen.h"
EGL_EXT_STUBS_HEADER_PATH = "../src/libGLESv2/egl_ext_stubs_autogen.h" EGL_EXT_STUBS_HEADER_PATH = "../src/libGLESv2/egl_ext_stubs_autogen.h"
...@@ -265,6 +266,30 @@ TEMPLATE_CL_ENTRY_POINT_WITH_RETURN = """\ ...@@ -265,6 +266,30 @@ TEMPLATE_CL_ENTRY_POINT_WITH_RETURN = """\
}} }}
""" """
TEMPLATE_CL_STUBS_HEADER = """\
// GENERATED FILE - DO NOT EDIT.
// Generated by {script_name} using data from {data_source_name}.
//
// Copyright 2021 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}_stubs_autogen.h: Stubs for {title} entry points.
#ifndef LIBGLESV2_{annotation_upper}_STUBS_AUTOGEN_H_
#define LIBGLESV2_{annotation_upper}_STUBS_AUTOGEN_H_
#include "angle_cl.h"
namespace cl
{{
class Thread;
{stubs}
}} // namespace cl
#endif // LIBGLESV2_{annotation_upper}_STUBS_AUTOGEN_H_
"""
TEMPLATE_EGL_STUBS_HEADER = """\ TEMPLATE_EGL_STUBS_HEADER = """\
// GENERATED FILE - DO NOT EDIT. // GENERATED FILE - DO NOT EDIT.
// Generated by {script_name} using data from {data_source_name}. // Generated by {script_name} using data from {data_source_name}.
...@@ -940,7 +965,7 @@ LIBCL_HEADER_INCLUDES = """\ ...@@ -940,7 +965,7 @@ LIBCL_HEADER_INCLUDES = """\
#ifndef CL_API_ENTRY #ifndef CL_API_ENTRY
# define CL_API_ENTRY ANGLE_EXPORT # define CL_API_ENTRY ANGLE_EXPORT
#endif #endif
#include <CL/cl.h> #include "angle_cl.h"
""" """
LIBCL_SOURCE_INCLUDES = """\ LIBCL_SOURCE_INCLUDES = """\
...@@ -1160,6 +1185,8 @@ TEMPLATE_RESOURCE_ID_TYPE_NAME_CASE = """\ ...@@ -1160,6 +1185,8 @@ TEMPLATE_RESOURCE_ID_TYPE_NAME_CASE = """\
case ResourceIDType::{resource_id_type}: case ResourceIDType::{resource_id_type}:
return "{resource_id_type}";""" return "{resource_id_type}";"""
CL_PACKED_TYPES = {}
EGL_PACKED_TYPES = { EGL_PACKED_TYPES = {
"EGLContext": "gl::Context *", "EGLContext": "gl::Context *",
"EGLConfig": "Config *", "EGLConfig": "Config *",
...@@ -1194,6 +1221,15 @@ def get_api_entry_def(api): ...@@ -1194,6 +1221,15 @@ def get_api_entry_def(api):
return "GL_APIENTRY" return "GL_APIENTRY"
def get_stubs_header_template(api):
if api == apis.CL:
return TEMPLATE_CL_STUBS_HEADER
elif api == apis.EGL:
return TEMPLATE_EGL_STUBS_HEADER
else:
return ""
def format_entry_point_decl(api, cmd_name, proto, params, is_explicit_context): def format_entry_point_decl(api, cmd_name, proto, params, is_explicit_context):
comma_if_needed = ", " if len(params) > 0 else "" comma_if_needed = ", " if len(params) > 0 else ""
stripped = strip_api_prefix(cmd_name) stripped = strip_api_prefix(cmd_name)
...@@ -1208,35 +1244,59 @@ def format_entry_point_decl(api, cmd_name, proto, params, is_explicit_context): ...@@ -1208,35 +1244,59 @@ def format_entry_point_decl(api, cmd_name, proto, params, is_explicit_context):
explicit_context_comma=", " if is_explicit_context and len(params) > 0 else "") explicit_context_comma=", " if is_explicit_context and len(params) > 0 else "")
def type_name_sep_index(param): # Returns index range of identifier in function parameter
space = param.rfind(" ") def find_name_range(param):
pointer = param.rfind("*")
return max(space, pointer) def is_allowed_in_identifier(char):
return char.isalpha() or char.isdigit() or char == "_"
# If type is a function declaration, only search in first parentheses
left_paren = param.find("(")
if left_paren >= 0:
min = left_paren + 1
end = param.index(")")
else:
min = 0
end = len(param)
# Find last identifier in search range
while end > min and not is_allowed_in_identifier(param[end - 1]):
end -= 1
if end == min:
raise ValueError
start = end - 1
while start > min and is_allowed_in_identifier(param[start - 1]):
start -= 1
return start, end
def just_the_type(param): def just_the_type(param):
if "*" in param: start, end = find_name_range(param)
return param[:type_name_sep_index(param) + 1].strip() return param[:start].strip() + param[end:].strip()
return param[:type_name_sep_index(param)].strip()
def just_the_name(param): def just_the_name(param):
start, end = find_name_range(param)
return param[start:end]
def get_name(param_string):
return param_string[type_name_sep_index(param_string) + 1:].strip()
if param.find("[]") != -1: def make_param(param_type, param_name):
param = param.replace("[]", "")
left_paren = param.find("(") def insert_name(param_type, param_name, pos):
if left_paren == -1: return param_type[:pos] + " " + param_name + param_type[pos:]
return get_name(param)
right_paren = param.index(")")
paren_content = param[left_paren + 1:right_paren]
return get_name(paren_content)
# If type is a function declaration, insert identifier before first closing parentheses
left_paren = param_type.find("(")
if left_paren >= 0:
right_paren = param_type.index(")")
return insert_name(param_type, param_name, right_paren)
def make_param(param_type, param_name): # If type is an array declaration, insert identifier before brackets
brackets = param_type.find("[")
if brackets >= 0:
return insert_name(param_type, param_name, brackets)
# Otherwise just append identifier
return param_type + " " + param_name return param_type + " " + param_name
...@@ -2365,8 +2425,8 @@ def get_egl_entry_point_labeled_object(ep_to_object, cmd_stripped, params, packe ...@@ -2365,8 +2425,8 @@ def get_egl_entry_point_labeled_object(ep_to_object, cmd_stripped, params, packe
return "Get%sIfValid(%s, %s)" % (category, display_param, found_param) return "Get%sIfValid(%s, %s)" % (category, display_param, found_param)
def write_egl_stubs_header(annotation, title, data_source, out_file, all_commands, commands, def write_stubs_header(api, annotation, title, data_source, out_file, all_commands, commands,
cmd_packed_egl_enums): cmd_packed_egl_enums, packed_param_types):
stubs = [] stubs = []
...@@ -2379,10 +2439,10 @@ def write_egl_stubs_header(annotation, title, data_source, out_file, all_command ...@@ -2379,10 +2439,10 @@ def write_egl_stubs_header(annotation, title, data_source, out_file, all_command
proto_text = "".join(proto.itertext()) proto_text = "".join(proto.itertext())
params = ["".join(param.itertext()) for param in command.findall('param')] params = ["".join(param.itertext()) for param in command.findall('param')]
return_type = proto_text[:-len(cmd_name)] return_type = proto_text[:-len(cmd_name)].strip()
internal_params = get_internal_params(apis.EGL, cmd_name, ["Thread *thread"] + params, internal_params = get_internal_params(apis.EGL, cmd_name, ["Thread *thread"] + params,
cmd_packed_egl_enums, EGL_PACKED_TYPES) cmd_packed_egl_enums, packed_param_types)
stubs.append("%s %s(%s);" % (return_type, strip_api_prefix(cmd_name), internal_params)) stubs.append("%s %s(%s);" % (return_type, strip_api_prefix(cmd_name), internal_params))
...@@ -2390,13 +2450,12 @@ def write_egl_stubs_header(annotation, title, data_source, out_file, all_command ...@@ -2390,13 +2450,12 @@ def write_egl_stubs_header(annotation, title, data_source, out_file, all_command
"annotation_lower": annotation.lower(), "annotation_lower": annotation.lower(),
"annotation_upper": annotation.upper(), "annotation_upper": annotation.upper(),
"data_source_name": data_source, "data_source_name": data_source,
"return_type": proto[:-len(cmd_name)],
"script_name": os.path.basename(sys.argv[0]), "script_name": os.path.basename(sys.argv[0]),
"stubs": "\n".join(stubs), "stubs": "\n".join(stubs),
"title": title, "title": title,
} }
output = TEMPLATE_EGL_STUBS_HEADER.format(**args) output = get_stubs_header_template(api).format(**args)
with open(out_file, "w") as f: with open(out_file, "w") as f:
f.write(output) f.write(output)
...@@ -2411,6 +2470,7 @@ def main(): ...@@ -2411,6 +2470,7 @@ def main():
EGL_GET_LABELED_OBJECT_DATA_PATH EGL_GET_LABELED_OBJECT_DATA_PATH
] + registry_xml.xml_inputs ] + registry_xml.xml_inputs
outputs = [ outputs = [
CL_STUBS_HEADER_PATH,
EGL_STUBS_HEADER_PATH, EGL_STUBS_HEADER_PATH,
EGL_EXT_STUBS_HEADER_PATH, EGL_EXT_STUBS_HEADER_PATH,
'../src/libOpenCL/libOpenCL_autogen.cpp', '../src/libOpenCL/libOpenCL_autogen.cpp',
...@@ -2800,6 +2860,8 @@ def main(): ...@@ -2800,6 +2860,8 @@ def main():
LIBCL_HEADER_INCLUDES, "libOpenCL", "cl.xml") LIBCL_HEADER_INCLUDES, "libOpenCL", "cl.xml")
write_file("cl", "CL", TEMPLATE_ENTRY_POINT_SOURCE, "\n".join(cl_defs), "cpp", write_file("cl", "CL", TEMPLATE_ENTRY_POINT_SOURCE, "\n".join(cl_defs), "cpp",
LIBCL_SOURCE_INCLUDES, "libOpenCL", "cl.xml") LIBCL_SOURCE_INCLUDES, "libOpenCL", "cl.xml")
write_stubs_header("CL", "cl", "CL", "cl.xml", CL_STUBS_HEADER_PATH, clxml.all_commands,
cl_commands, CLEntryPoints.get_packed_enums(), CL_PACKED_TYPES)
# EGL # EGL
eglxml = registry_xml.RegistryXML('egl.xml', 'egl_angle_ext.xml') eglxml = registry_xml.RegistryXML('egl.xml', 'egl_angle_ext.xml')
...@@ -2843,8 +2905,8 @@ def main(): ...@@ -2843,8 +2905,8 @@ def main():
EGL_HEADER_INCLUDES, "libGLESv2", "egl.xml") EGL_HEADER_INCLUDES, "libGLESv2", "egl.xml")
write_file("egl", "EGL", TEMPLATE_ENTRY_POINT_SOURCE, "\n".join(egl_defs), "cpp", write_file("egl", "EGL", TEMPLATE_ENTRY_POINT_SOURCE, "\n".join(egl_defs), "cpp",
EGL_SOURCE_INCLUDES, "libGLESv2", "egl.xml") EGL_SOURCE_INCLUDES, "libGLESv2", "egl.xml")
write_egl_stubs_header("egl", "EGL", "egl.xml", EGL_STUBS_HEADER_PATH, eglxml.all_commands, write_stubs_header("EGL", "egl", "EGL", "egl.xml", EGL_STUBS_HEADER_PATH, eglxml.all_commands,
egl_commands, EGLEntryPoints.get_packed_enums()) egl_commands, EGLEntryPoints.get_packed_enums(), EGL_PACKED_TYPES)
eglxml.AddExtensionCommands(registry_xml.supported_egl_extensions, ['egl']) eglxml.AddExtensionCommands(registry_xml.supported_egl_extensions, ['egl'])
egl_ext_decls = [] egl_ext_decls = []
...@@ -2882,9 +2944,9 @@ def main(): ...@@ -2882,9 +2944,9 @@ def main():
"cpp", EGL_EXT_SOURCE_INCLUDES, "libGLESv2", "egl.xml and egl_angle_ext.xml") "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", write_validation_header("EGL", "EGL", egl_validation_protos, "egl.xml and egl_angle_ext.xml",
TEMPLATE_EGL_VALIDATION_HEADER) TEMPLATE_EGL_VALIDATION_HEADER)
write_egl_stubs_header("egl_ext", "EXT extension", "egl.xml and egl_angle_ext.xml", 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, EGL_EXT_STUBS_HEADER_PATH, eglxml.all_commands, egl_ext_commands,
EGLEntryPoints.get_packed_enums()) EGLEntryPoints.get_packed_enums(), EGL_PACKED_TYPES)
# WGL # WGL
wglxml = registry_xml.RegistryXML('wgl.xml') wglxml = registry_xml.RegistryXML('wgl.xml')
......
...@@ -157,8 +157,26 @@ libangle_gpu_info_util_ios_sources = [ ...@@ -157,8 +157,26 @@ libangle_gpu_info_util_ios_sources = [
] ]
libangle_includes = [ libangle_includes = [
"include/angle_cl.h",
"include/angle_gl.h", "include/angle_gl.h",
"include/export.h", "include/export.h",
"include/CL/cl.h",
"include/CL/cl_d3d10.h",
"include/CL/cl_d3d11.h",
"include/CL/cl_dx9_media_sharing.h",
"include/CL/cl_dx9_media_sharing_intel.h",
"include/CL/cl_egl.h",
"include/CL/cl_ext.h",
"include/CL/cl_ext_intel.h",
"include/CL/cl_gl.h",
"include/CL/cl_gl_ext.h",
"include/CL/cl_half.h",
"include/CL/cl_icd.h",
"include/CL/cl_layer.h",
"include/CL/cl_platform.h",
"include/CL/cl_va_api_media_sharing_intel.h",
"include/CL/cl_version.h",
"include/CL/opencl.h",
"include/EGL/egl.h", "include/EGL/egl.h",
"include/EGL/eglext.h", "include/EGL/eglext.h",
"include/EGL/eglext_angle.h", "include/EGL/eglext_angle.h",
...@@ -493,6 +511,8 @@ if (is_win) { ...@@ -493,6 +511,8 @@ if (is_win) {
} }
libglesv2_sources = [ libglesv2_sources = [
"src/libGLESv2/cl_stubs.cpp",
"src/libGLESv2/cl_stubs_autogen.h",
"src/libGLESv2/egl_ext_stubs.cpp", "src/libGLESv2/egl_ext_stubs.cpp",
"src/libGLESv2/egl_ext_stubs_autogen.h", "src/libGLESv2/egl_ext_stubs_autogen.h",
"src/libGLESv2/egl_stubs.cpp", "src/libGLESv2/egl_stubs.cpp",
......
...@@ -25,21 +25,13 @@ angle_source_set("cl_includes") { ...@@ -25,21 +25,13 @@ angle_source_set("cl_includes") {
"../../include/CL/cl_va_api_media_sharing_intel.h", "../../include/CL/cl_va_api_media_sharing_intel.h",
"../../include/CL/cl_version.h", "../../include/CL/cl_version.h",
"../../include/CL/opencl.h", "../../include/CL/opencl.h",
"../../include/angle_cl.h",
"../../include/export.h", "../../include/export.h",
] ]
} }
angle_shared_library("OpenCL") { angle_shared_library("OpenCL") {
defines = [ defines = [ "LIBCL_IMPLEMENTATION" ]
"CL_TARGET_OPENCL_VERSION=300",
"CL_USE_DEPRECATED_OPENCL_2_2_APIS",
"CL_USE_DEPRECATED_OPENCL_2_1_APIS",
"CL_USE_DEPRECATED_OPENCL_2_0_APIS",
"CL_USE_DEPRECATED_OPENCL_1_2_APIS",
"CL_USE_DEPRECATED_OPENCL_1_1_APIS",
"CL_USE_DEPRECATED_OPENCL_1_0_APIS",
"LIBCL_IMPLEMENTATION",
]
sources = [ sources = [
"cl_loader.h", "cl_loader.h",
"cl_loader_autogen.cpp", "cl_loader_autogen.cpp",
......
...@@ -14,7 +14,10 @@ ...@@ -14,7 +14,10 @@
#ifndef CL_API_ENTRY #ifndef CL_API_ENTRY
# define CL_API_ENTRY ANGLE_EXPORT # define CL_API_ENTRY ANGLE_EXPORT
#endif #endif
#include <CL/cl_icd.h> #include "angle_cl.h"
// 'angle_cl.h' has to be included before this to enable CL defines
#include "CL/cl_icd.h"
ANGLE_NO_EXPORT extern cl_icd_dispatch cl_loader; ANGLE_NO_EXPORT extern cl_icd_dispatch cl_loader;
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
#ifndef CL_API_ENTRY #ifndef CL_API_ENTRY
# define CL_API_ENTRY ANGLE_EXPORT # define CL_API_ENTRY ANGLE_EXPORT
#endif #endif
#include <CL/cl.h> #include "angle_cl.h"
extern "C" { extern "C" {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment