Skip to content
Snippets Groups Projects
Commit 9f182592 authored by David Heidelberg's avatar David Heidelberg Committed by Natanael Copa
Browse files

main/mesa: revert rusticl for freedreno


The MR is still in draft and could have potentially impact other
parts of Mesa (GL/VK), so let's rather wait for the exciting news when
fully verified.

Signed-off-by: default avatarDavid Heidelberg <david@ixit.cz>
parent d87cddcb
No related branches found
No related tags found
1 merge request!56847main/mesa: revert rusticl for freedreno
Pipeline #199637 skipped
From 11af8ccacff8987af8e62a3618e238f16b0fe369 Mon Sep 17 00:00:00 2001
From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Date: Sat, 21 Oct 2023 14:36:00 +0000
Subject: [PATCH 01/10] freedreno/a6xx: provide clear_buffer implementation
Provide default unoptimized clear_buffer implementation, required for
rusticl.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
src/gallium/drivers/freedreno/a6xx/fd6_blitter.cc | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/gallium/drivers/freedreno/a6xx/fd6_blitter.cc b/src/gallium/drivers/freedreno/a6xx/fd6_blitter.cc
index 65264cbdda1db..bb3e04fc3bf6c 100644
--- a/src/gallium/drivers/freedreno/a6xx/fd6_blitter.cc
+++ b/src/gallium/drivers/freedreno/a6xx/fd6_blitter.cc
@@ -32,6 +32,7 @@
#include "util/u_dump.h"
#include "util/u_log.h"
#include "util/u_surface.h"
+#include "util/u_transfer.h"
#include "freedreno_blitter.h"
#include "freedreno_fence.h"
@@ -1404,6 +1405,7 @@ fd6_blitter_init(struct pipe_context *pctx)
return;
pctx->clear_texture = fd6_clear_texture<CHIP>;
+ pctx->clear_buffer = u_default_clear_buffer;
ctx->blit = fd6_blit<CHIP>;
}
--
GitLab
From fd1f25c0b7c16ede11df46118c4f88e7eecb87d3 Mon Sep 17 00:00:00 2001
From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Date: Sat, 21 Oct 2023 14:39:49 +0000
Subject: [PATCH 02/10] freedreno/a6xx: implement get_compute_state_info
Provide get_compute_state_info() implementation for rusticl.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
.../drivers/freedreno/a6xx/fd6_compute.cc | 21 +++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/src/gallium/drivers/freedreno/a6xx/fd6_compute.cc b/src/gallium/drivers/freedreno/a6xx/fd6_compute.cc
index 5b94f4567ae71..c325891424c58 100644
--- a/src/gallium/drivers/freedreno/a6xx/fd6_compute.cc
+++ b/src/gallium/drivers/freedreno/a6xx/fd6_compute.cc
@@ -259,6 +259,26 @@ fd6_compute_state_delete(struct pipe_context *pctx, void *_hwcso)
free(hwcso);
}
+static void
+fd6_get_compute_state_info(struct pipe_context *pctx, void *cso, struct pipe_compute_state_object_info *info)
+{
+ static struct ir3_shader_key key; /* static is implicitly zeroed */
+ struct fd6_compute_state *cs = (struct fd6_compute_state *)cso;
+ struct ir3_shader_state *hwcso = (struct ir3_shader_state *)cs->hwcso;
+ struct ir3_shader_variant *v = ir3_shader_variant(ir3_get_shader(hwcso), key, false, &pctx->debug);
+ struct fd_context *ctx = fd_context(pctx);
+
+ if (ctx->screen->info->a6xx.supports_double_threadsize) {
+ info->max_threads = 2048;
+ info->preferred_simd_size = 128;
+ } else {
+ info->max_threads = 1024;
+ info->preferred_simd_size = 64;
+ }
+ info->private_memory = v->pvtmem_size;
+ info->simd_sizes = info->preferred_simd_size;
+}
+
template <chip CHIP>
void
fd6_compute_init(struct pipe_context *pctx)
@@ -269,6 +289,7 @@ fd6_compute_init(struct pipe_context *pctx)
ctx->launch_grid = fd6_launch_grid<CHIP>;
pctx->create_compute_state = fd6_compute_state_create;
pctx->delete_compute_state = fd6_compute_state_delete;
+ pctx->get_compute_state_info = fd6_get_compute_state_info;
}
/* Teach the compiler about needed variants: */
--
GitLab
From 61fabb6030da2974f85547413f9305b09735fd75 Mon Sep 17 00:00:00 2001
From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Date: Sat, 21 Oct 2023 14:40:57 +0000
Subject: [PATCH 03/10] freedreno/a6xx: handle MESA_SHADER_KERNEL in
fd6_emit_shader
The fd6_emit_shader() contains the `if (type == MESA_SHADER_COMPUTE)
type = MESA_SHADER_COMPUTE` construction, which is obviously useless.
Change that to treat MESA_SHADER_KERNEL as MESA_SHADER_COMPUTE shaders.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
src/gallium/drivers/freedreno/a6xx/fd6_program.cc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/gallium/drivers/freedreno/a6xx/fd6_program.cc b/src/gallium/drivers/freedreno/a6xx/fd6_program.cc
index 4ee1852b75756..183bba9b1af84 100644
--- a/src/gallium/drivers/freedreno/a6xx/fd6_program.cc
+++ b/src/gallium/drivers/freedreno/a6xx/fd6_program.cc
@@ -119,7 +119,7 @@ fd6_emit_shader(struct fd_context *ctx, struct fd_ringbuffer *ring,
#endif
gl_shader_stage type = so->type;
- if (type == MESA_SHADER_COMPUTE)
+ if (type == MESA_SHADER_KERNEL)
type = MESA_SHADER_COMPUTE;
enum a6xx_threadsize thrsz =
--
GitLab
From 7f406db42a0c4552d10d46f40569104a540dd4bb Mon Sep 17 00:00:00 2001
From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Date: Sat, 21 Oct 2023 14:44:04 +0000
Subject: [PATCH 04/10] freedreno/ir3: treat MESA_SHADER_KERNEL in the same way
as compute
In ir3_shader_descriptor_set() tread MESA_SHADER_KERNEL shaders in the
same way, as PIPE_SHADER_COMPUTE shaders, return 0.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
src/gallium/drivers/freedreno/ir3/ir3_descriptor.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/gallium/drivers/freedreno/ir3/ir3_descriptor.h b/src/gallium/drivers/freedreno/ir3/ir3_descriptor.h
index fa14f855134ff..8f72961d8e0de 100644
--- a/src/gallium/drivers/freedreno/ir3/ir3_descriptor.h
+++ b/src/gallium/drivers/freedreno/ir3/ir3_descriptor.h
@@ -59,6 +59,7 @@ ir3_shader_descriptor_set(enum pipe_shader_type shader)
case PIPE_SHADER_GEOMETRY: return 3;
case PIPE_SHADER_FRAGMENT: return 4;
case PIPE_SHADER_COMPUTE: return 0;
+ case MESA_SHADER_KERNEL: return 0;
default:
unreachable("bad shader stage");
return ~0;
--
GitLab
From ad264249c20ce03094f85b5698f46474767dccd1 Mon Sep 17 00:00:00 2001
From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Date: Sat, 30 Sep 2023 10:36:19 +0000
Subject: [PATCH 05/10] rusticl: enable freedreno
To really use the driver, specify the environment variable:
export RUSTICL_ENABLE=msm
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
src/gallium/targets/rusticl/meson.build | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/gallium/targets/rusticl/meson.build b/src/gallium/targets/rusticl/meson.build
index b2963fe6dfa76..234eba9fb2fb7 100644
--- a/src/gallium/targets/rusticl/meson.build
+++ b/src/gallium/targets/rusticl/meson.build
@@ -50,6 +50,7 @@ librusticl = shared_library(
],
dependencies : [
driver_asahi,
+ driver_freedreno,
driver_iris,
driver_nouveau,
driver_r600,
--
GitLab
From 1904d5d6ea9c22bc979a26d972ee8ad7315ed4ee Mon Sep 17 00:00:00 2001
From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Date: Sun, 22 Oct 2023 01:59:12 +0000
Subject: [PATCH 06/10] ir3: lower the hadd operations
There do not seem to be instructions for the ihadd/uhadd NIR operations.
Lower them to simpler ops.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
src/freedreno/ir3/ir3_compiler.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/freedreno/ir3/ir3_compiler.c b/src/freedreno/ir3/ir3_compiler.c
index 34b08b2fe0bbc..03973c691c761 100644
--- a/src/freedreno/ir3/ir3_compiler.c
+++ b/src/freedreno/ir3/ir3_compiler.c
@@ -121,6 +121,9 @@ static const nir_shader_compiler_options ir3_base_options = {
.lower_cs_local_index_to_id = true,
.lower_wpos_pntc = true,
+ .lower_hadd = true,
+ .lower_hadd64 = true,
+
.lower_int64_options = (nir_lower_int64_options)~0,
.lower_doubles_options = (nir_lower_doubles_options)~0,
};
--
GitLab
From c4c2b81d17de57a7e77d5e2cce8b3266406e71ff Mon Sep 17 00:00:00 2001
From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Date: Sun, 22 Oct 2023 04:01:23 +0000
Subject: [PATCH 07/10] ir3: handle nir_intrinsic_load_ubo in
lower_wide_load_store()
Unlike other load intrinsics, which use simple address as the only source, the
load_ubo intrinsic uses index and offset sources. Modify lower_wide_load_store
accordingly, to handle the load_ubo intrinsic.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
.../ir3/ir3_nir_lower_wide_load_store.c | 34 +++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/src/freedreno/ir3/ir3_nir_lower_wide_load_store.c b/src/freedreno/ir3/ir3_nir_lower_wide_load_store.c
index a3fb2eec7d97b..8b36ef5e9e2f7 100644
--- a/src/freedreno/ir3/ir3_nir_lower_wide_load_store.c
+++ b/src/freedreno/ir3/ir3_nir_lower_wide_load_store.c
@@ -79,6 +79,40 @@ lower_wide_load_store(nir_builder *b, nir_instr *instr, void *unused)
}
return NIR_LOWER_INSTR_PROGRESS_REPLACE;
+ } else if (intr->intrinsic == nir_intrinsic_load_ubo) {
+ unsigned num_comp = nir_intrinsic_dest_components(intr);
+ unsigned bit_size = intr->def.bit_size;
+ nir_def *offset = intr->src[1].ssa;
+ nir_def *components[num_comp];
+
+ for (unsigned off = 0; off < num_comp;) {
+ unsigned c = MIN2(num_comp - off, 4);
+
+ nir_intrinsic_instr *load =
+ nir_intrinsic_instr_create(b->shader, intr->intrinsic);
+ load->num_components = c;
+ load->src[0] = intr->src[0];
+ load->src[1] = nir_src_for_ssa(offset);
+ nir_intrinsic_set_align(load, nir_intrinsic_align(intr), 0);
+ nir_def_init(&load->instr, &load->def, c, bit_size);
+ if (nir_intrinsic_has_range(intr))
+ nir_intrinsic_set_range(load, nir_intrinsic_range(intr));
+ if (nir_intrinsic_has_range_base(intr))
+ nir_intrinsic_set_range_base(load, nir_intrinsic_range_base(intr));
+ if (nir_intrinsic_has_base(intr))
+ nir_intrinsic_set_base(load, nir_intrinsic_base(intr));
+ nir_builder_instr_insert(b, &load->instr);
+
+ offset = nir_iadd(b,
+ nir_imm_intN_t(b, (c * bit_size) / 8, offset->bit_size),
+ offset);
+
+ for (unsigned i = 0; i < c; i++) {
+ components[off++] = nir_channel(b, &load->def, i);
+ }
+ }
+
+ return nir_build_alu_src_arr(b, nir_op_vec(num_comp), components);
} else {
unsigned num_comp = nir_intrinsic_dest_components(intr);
unsigned bit_size = intr->def.bit_size;
--
GitLab
From af0ae59947b35710974eaa336661d167738abd8e Mon Sep 17 00:00:00 2001
From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Date: Sun, 22 Oct 2023 09:47:41 +0000
Subject: [PATCH 08/10] ir3: fix shift amount for 8-bit shifts
Follow the 16-bit approach and convert shift amount to 8b for 8b shift
instructions.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
src/freedreno/ir3/ir3_compiler_nir.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/freedreno/ir3/ir3_compiler_nir.c b/src/freedreno/ir3/ir3_compiler_nir.c
index d1f36c7f493ef..0da15340814b1 100644
--- a/src/freedreno/ir3/ir3_compiler_nir.c
+++ b/src/freedreno/ir3/ir3_compiler_nir.c
@@ -294,10 +294,12 @@ static struct ir3_instruction *
resize_shift_amount(struct ir3_context *ctx, struct ir3_instruction *src,
unsigned bs)
{
- if (bs != 16)
+ if (bs == 16)
+ return ir3_COV(ctx->block, src, TYPE_U32, TYPE_U16);
+ else if (bs == 8)
+ return ir3_COV(ctx->block, src, TYPE_U32, TYPE_U8);
+ else
return src;
-
- return ir3_COV(ctx->block, src, TYPE_U32, TYPE_U16);
}
static void
--
GitLab
From 67a5854ff5110e86d7c3a2c498c3eef71a58ee77 Mon Sep 17 00:00:00 2001
From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Date: Sun, 22 Oct 2023 18:05:23 +0000
Subject: [PATCH 09/10] ir3/a6xx: fix ldg/stg of ulong2 and ulong4 data
Partially revert the commit f4c9e9329cf ("ir3/a6xx: Fix immediate
offset stg/ldg path").
There is no need to multiply the immediate offsets by 4. Doing so
results in loading and/or storing the data at wrong locations.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
src/freedreno/ir3/ir3_a6xx.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/freedreno/ir3/ir3_a6xx.c b/src/freedreno/ir3/ir3_a6xx.c
index 1592a56a397c0..33b70323678c6 100644
--- a/src/freedreno/ir3/ir3_a6xx.c
+++ b/src/freedreno/ir3/ir3_a6xx.c
@@ -343,7 +343,7 @@ emit_intrinsic_load_global_ir3(struct ir3_context *ctx,
nir_src_as_int(intr->src[1]) > -(1 << 10);
if (const_offset_in_bounds) {
- load = ir3_LDG(b, addr, 0, create_immed(b, nir_src_as_int(intr->src[1]) * 4),
+ load = ir3_LDG(b, addr, 0, create_immed(b, nir_src_as_int(intr->src[1])),
0, create_immed(b, dest_components), 0);
} else {
offset = ir3_get_src(ctx, &intr->src[1])[0];
@@ -386,7 +386,7 @@ emit_intrinsic_store_global_ir3(struct ir3_context *ctx,
if (const_offset_in_bounds) {
stg = ir3_STG(b, addr, 0,
- create_immed(b, nir_src_as_int(intr->src[2]) * 4), 0,
+ create_immed(b, nir_src_as_int(intr->src[2])), 0,
value, 0,
create_immed(b, ncomp), 0);
} else {
--
GitLab
From 4fc3f74ef614cdd465b183db077f647e5c9e7aa9 Mon Sep 17 00:00:00 2001
From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Date: Mon, 13 Nov 2023 21:24:16 +0200
Subject: [PATCH 10/10] freedreno/drm: fallback to default BO allocation if
heap alloc fails
Allow fd_bo_heap_alloc() to return NULL if the heap is exausted (or
fragmented) instead of segfaulting. Then handle the error properly in
bo_new().
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
src/freedreno/drm/freedreno_bo.c | 8 +++++---
src/freedreno/drm/freedreno_bo_heap.c | 16 +++++++++++-----
2 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/src/freedreno/drm/freedreno_bo.c b/src/freedreno/drm/freedreno_bo.c
index efde63ec73fca..1415cb66ed9a5 100644
--- a/src/freedreno/drm/freedreno_bo.c
+++ b/src/freedreno/drm/freedreno_bo.c
@@ -132,9 +132,11 @@ bo_new(struct fd_device *dev, uint32_t size, uint32_t flags,
if (size < FD_BO_HEAP_BLOCK_SIZE) {
if ((flags == 0) && dev->default_heap)
- return fd_bo_heap_alloc(dev->default_heap, size);
- if ((flags == RING_FLAGS) && dev->ring_heap)
- return fd_bo_heap_alloc(dev->ring_heap, size);
+ bo = fd_bo_heap_alloc(dev->default_heap, size);
+ else if ((flags == RING_FLAGS) && dev->ring_heap)
+ bo = fd_bo_heap_alloc(dev->ring_heap, size);
+ if (bo)
+ return bo;
}
/* demote cached-coherent to WC if not supported: */
diff --git a/src/freedreno/drm/freedreno_bo_heap.c b/src/freedreno/drm/freedreno_bo_heap.c
index dc1af739d23d4..64c498255e669 100644
--- a/src/freedreno/drm/freedreno_bo_heap.c
+++ b/src/freedreno/drm/freedreno_bo_heap.c
@@ -211,10 +211,6 @@ fd_bo_heap_alloc(struct fd_bo_heap *heap, uint32_t size)
{
heap_clean(heap, true);
- struct sa_bo *s = calloc(1, sizeof(*s));
-
- s->heap = heap;
-
/* util_vma does not like zero byte allocations, which we get, for
* ex, with the initial query buffer allocation on pre-a5xx:
*/
@@ -229,7 +225,17 @@ fd_bo_heap_alloc(struct fd_bo_heap *heap, uint32_t size)
* (The 8k threshold is just a random guess, but seems to work ok)
*/
heap->heap.alloc_high = (size <= 8 * 1024);
- s->offset = util_vma_heap_alloc(&heap->heap, size, SUBALLOC_ALIGNMENT);
+ uint64_t offset = util_vma_heap_alloc(&heap->heap, size, SUBALLOC_ALIGNMENT);
+ if (!offset) {
+ simple_mtx_unlock(&heap->lock);
+ return NULL;
+ }
+
+ struct sa_bo *s = calloc(1, sizeof(*s));
+
+ s->heap = heap;
+ s->offset = offset;
+
assert((s->offset / FD_BO_HEAP_BLOCK_SIZE) == (s->offset + size - 1) / FD_BO_HEAP_BLOCK_SIZE);
unsigned idx = block_idx(s);
if (HEAP_DEBUG)
--
GitLab
From 8cb7b0ed9074493faca6c1b57b95ec1bf5e12bd3 Mon Sep 17 00:00:00 2001
From: David Heidelberg <david.heidelberg@collabora.com>
Date: Wed, 6 Dec 2023 21:49:06 +0100
Subject: [PATCH] freedreno: implement PIPE_CAP_TIMER_RESOLUTION
Allows rusticl to create queues with profiling enabled.
Fixes: 660f2eabe11 ("gallium: add PIPE_CAP_TIMER_RESOLUTION")
Signed-off-by: David Heidelberg <david.heidelberg@collabora.com>
---
src/gallium/drivers/freedreno/freedreno_screen.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/gallium/drivers/freedreno/freedreno_screen.c b/src/gallium/drivers/freedreno/freedreno_screen.c
index 6cadea8c2eb61..e75d21eadee38 100644
--- a/src/gallium/drivers/freedreno/freedreno_screen.c
+++ b/src/gallium/drivers/freedreno/freedreno_screen.c
@@ -590,6 +590,8 @@ fd_screen_get_param(struct pipe_screen *pscreen, enum pipe_cap param)
/* only a4xx, requires new enough kernel so we know max_freq: */
return (screen->max_freq > 0) &&
(is_a4xx(screen) || is_a5xx(screen) || is_a6xx(screen));
+ case PIPE_CAP_TIMER_RESOLUTION:
+ return DIV_ROUND_UP(1000000000ull, 19200000); /* RBBM timer */
case PIPE_CAP_QUERY_BUFFER_OBJECT:
case PIPE_CAP_QUERY_SO_OVERFLOW:
case PIPE_CAP_QUERY_PIPELINE_STATISTICS_SINGLE:
--
GitLab
# Maintainer: Natanael Copa <ncopa@alpinelinux.org> # Maintainer: Natanael Copa <ncopa@alpinelinux.org>
pkgname=mesa pkgname=mesa
pkgver=23.3.0 pkgver=23.3.0
pkgrel=0 pkgrel=1
pkgdesc="Mesa DRI OpenGL library" pkgdesc="Mesa DRI OpenGL library"
url="https://www.mesa3d.org" url="https://www.mesa3d.org"
arch="all" arch="all"
...@@ -63,8 +63,6 @@ makedepends=" ...@@ -63,8 +63,6 @@ makedepends="
" "
source=" source="
https://mesa.freedesktop.org/archive/mesa-${pkgver/_/-}.tar.xz https://mesa.freedesktop.org/archive/mesa-${pkgver/_/-}.tar.xz
25840.patch
26554.patch
no-unlink-megadrivers.patch no-unlink-megadrivers.patch
" "
replaces="mesa-dricore" replaces="mesa-dricore"
...@@ -368,7 +366,5 @@ _vulkan_layers() { ...@@ -368,7 +366,5 @@ _vulkan_layers() {
sha512sums=" sha512sums="
3568b571974a2c6c0c3a4cc614c851729962e39926f0af906ba91604f9d43c0618db7df004cbec5d9e5dbade2d6bde818608c9aa1686183994d68ad3e6ac4521 mesa-23.3.0.tar.xz 3568b571974a2c6c0c3a4cc614c851729962e39926f0af906ba91604f9d43c0618db7df004cbec5d9e5dbade2d6bde818608c9aa1686183994d68ad3e6ac4521 mesa-23.3.0.tar.xz
6aa65cc4b9ae9b6c84b73116dedf6ac34434b23be27351488e6d85e988aa90052e0299c6c7e102e76107871581de056a1c0abccb042dad8d1e3954365adb76a7 25840.patch
1de2771e86372758126b7815a35b892e46c5e73ee6d893384d98bfa504778a3c06fbac3ea93023c039e68c91e60689211d4c0d9304e441824895522b0214cfb8 26554.patch
e3d39d61b14188cd5a22e19e8c065650995f85943e6c42ec5775db5b76a9ebe8913253a8b9ac32056ac13a2c357cd951f867ecb39976c52f7d014dcc1a2d87dc no-unlink-megadrivers.patch e3d39d61b14188cd5a22e19e8c065650995f85943e6c42ec5775db5b76a9ebe8913253a8b9ac32056ac13a2c357cd951f867ecb39976c52f7d014dcc1a2d87dc no-unlink-megadrivers.patch
" "
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment