Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
glslang
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Chen Yisong
glslang
Commits
0b391370
Commit
0b391370
authored
Dec 05, 2013
by
John Kessenich
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement location overlap error checking.
git-svn-id:
https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@24376
e7fa87d3-cd2b-0410-9028-fcbf551c1848
parent
2f15597a
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
146 additions
and
13 deletions
+146
-13
300layout.frag
Test/300layout.frag
+4
-0
300layout.vert
Test/300layout.vert
+4
-1
400.frag
Test/400.frag
+1
-1
430.vert
Test/430.vert
+11
-3
300layout.frag.out
Test/baseResults/300layout.frag.out
+6
-1
300layout.vert.out
Test/baseResults/300layout.vert.out
+5
-1
400.frag.out
Test/baseResults/400.frag.out
+1
-1
430.vert.out
Test/baseResults/430.vert.out
+6
-2
specExamples.frag.out
Test/baseResults/specExamples.frag.out
+2
-1
revision.h
glslang/Include/revision.h
+2
-2
ParseHelper.cpp
glslang/MachineIndependent/ParseHelper.cpp
+4
-0
linkValidate.cpp
glslang/MachineIndependent/linkValidate.cpp
+91
-0
localintermediate.h
glslang/MachineIndependent/localintermediate.h
+9
-0
No files found.
Test/300layout.frag
View file @
0b391370
...
@@ -13,3 +13,7 @@ void main()
...
@@ -13,3 +13,7 @@ void main()
p
=
pos
;
p
=
pos
;
q
[
1
]
=
pos
;
q
[
1
]
=
pos
;
}
}
layout
(
location
=
40
)
out
float
ca
[
4
];
layout
(
location
=
41
)
out
float
cb
[
2
];
// ERROR, overlap
layout
(
location
=
39
)
out
float
cc
[
6
];
// ERROR, overlap
Test/300layout.vert
View file @
0b391370
...
@@ -5,7 +5,7 @@ struct s { vec4 v; };
...
@@ -5,7 +5,7 @@ struct s { vec4 v; };
layout
(
location
=
7
)
in
vec3
c
;
layout
(
location
=
7
)
in
vec3
c
;
layout
(
LocatioN
=
3
)
in
vec4
p
;
layout
(
LocatioN
=
3
)
in
vec4
p
;
layout
(
LocatioN
=
9
)
in
vec4
q
[
4
];
// ERROR, no array
layout
(
LocatioN
=
9
)
in
vec4
q
[
4
];
// ERROR, no array
layout
(
LocatioN
=
10
)
in
s
r
[
4
];
// ERROR, no struct
layout
(
LocatioN
=
10
)
in
s
r
[
4
];
// ERROR, no struct
, ERROR, location overlap
out
vec4
pos
;
out
vec4
pos
;
out
vec3
color
;
out
vec3
color
;
...
@@ -52,3 +52,6 @@ shared vec4 compute_only; // ERROR
...
@@ -52,3 +52,6 @@ shared vec4 compute_only; // ERROR
layout
(
packed
)
uniform
;
layout
(
packed
)
uniform
;
layout
(
packed
)
float
aoeuntaoeu
;
layout
(
packed
)
float
aoeuntaoeu
;
layout
(
location
=
40
)
in
float
cd
;
layout
(
location
=
37
)
in
mat4x3
ce
;
// ERROR, overlap
Test/400.frag
View file @
0b391370
...
@@ -33,7 +33,7 @@ layout(location = 4) in vec4 vl; // ERROR, not supported
...
@@ -33,7 +33,7 @@ layout(location = 4) in vec4 vl; // ERROR, not supported
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_separate_shader_objects : enable
#endif
#endif
layout
(
location
=
4
)
in
vec4
vl2
;
layout
(
location
=
6
)
in
vec4
vl2
;
layout
(
location
=
3
)
uniform
vec3
uv3
;
layout
(
location
=
3
)
uniform
vec3
uv3
;
...
...
Test/430.vert
View file @
0b391370
...
@@ -2,7 +2,7 @@
...
@@ -2,7 +2,7 @@
layout
(
location
=
3
)
vec4
v4
;
// ERROR
layout
(
location
=
3
)
vec4
v4
;
// ERROR
layout
(
location
=
3
)
uniform
vec4
uv4
;
layout
(
location
=
4
)
uniform
vec4
uv4
;
layout
(
location
=
2
)
in
inb1
{
vec4
v
;
}
b1
;
// ERROR
layout
(
location
=
2
)
in
inb1
{
vec4
v
;
}
b1
;
// ERROR
layout
(
location
=
2
)
out
outb1
{
vec4
v
;
}
b2
;
// ERROR
layout
(
location
=
2
)
out
outb1
{
vec4
v
;
}
b2
;
// ERROR
...
@@ -30,4 +30,13 @@ void foo3(invariant vec4 v4, // ERROR
...
@@ -30,4 +30,13 @@ void foo3(invariant vec4 v4, // ERROR
layout
(
location
=
3
)
vec2
v2
,
// ERROR
layout
(
location
=
3
)
vec2
v2
,
// ERROR
centroid
vec3
cv3
)
// ERROR
centroid
vec3
cv3
)
// ERROR
{
{
}
}
\ No newline at end of file
struct
S
{
mat3x2
m
[
7
];
// needs 7*3 locations
float
f
;
// needs 1 location
};
// needs 22 locations
layout
(
location
=
10
)
out
S
cs
[
2
];
// 10 through 10 + 2 * 22 - 1 = 53
layout
(
location
=
54
)
out
float
cf
;
layout
(
location
=
53
)
out
float
cg
;
// ERROR, collision at 31
Test/baseResults/300layout.frag.out
View file @
0b391370
300layout.frag
300layout.frag
ERROR: 0:4: 'location qualifier on input' : not supported in this stage: fragment
ERROR: 0:4: 'location qualifier on input' : not supported in this stage: fragment
ERROR: 1 compilation errors. No code generated.
ERROR: 0:18: 'location' : repeated use of location 41
ERROR: 0:19: 'location' : repeated use of location 40
ERROR: 3 compilation errors. No code generated.
ERROR: node is still EOpNull!
ERROR: node is still EOpNull!
...
@@ -25,6 +27,9 @@ ERROR: node is still EOpNull!
...
@@ -25,6 +27,9 @@ ERROR: node is still EOpNull!
0:? 'c' (layout(location=1 ) out mediump 4-component vector of float)
0:? 'c' (layout(location=1 ) out mediump 4-component vector of float)
0:? 'p' (layout(location=3 ) out mediump 4-component vector of float)
0:? 'p' (layout(location=3 ) out mediump 4-component vector of float)
0:? 'q' (layout(location=4 ) out 2-element array of mediump 4-component vector of float)
0:? 'q' (layout(location=4 ) out 2-element array of mediump 4-component vector of float)
0:? 'ca' (layout(location=40 ) out 4-element array of mediump float)
0:? 'cb' (layout(location=41 ) out 2-element array of mediump float)
0:? 'cc' (layout(location=39 ) out 6-element array of mediump float)
Linked fragment stage:
Linked fragment stage:
...
...
Test/baseResults/300layout.vert.out
View file @
0b391370
...
@@ -2,6 +2,7 @@
...
@@ -2,6 +2,7 @@
ERROR: 0:7: 'vertex input arrays' : not supported with this profile: es
ERROR: 0:7: 'vertex input arrays' : not supported with this profile: es
ERROR: 0:8: 'in' : cannot be a structure or array
ERROR: 0:8: 'in' : cannot be a structure or array
ERROR: 0:8: 'vertex input arrays' : not supported with this profile: es
ERROR: 0:8: 'vertex input arrays' : not supported with this profile: es
ERROR: 0:8: 'location' : repeated use of location 10
ERROR: 0:12: 'badm4' : cannot specify matrix layout on a variable declaration
ERROR: 0:12: 'badm4' : cannot specify matrix layout on a variable declaration
ERROR: 0:12: 'badm4' : cannot specify packing on a variable declaration
ERROR: 0:12: 'badm4' : cannot specify packing on a variable declaration
ERROR: 0:19: 'badf' : member of uniform block cannot have an auxiliary or interpolation qualifier
ERROR: 0:19: 'badf' : member of uniform block cannot have an auxiliary or interpolation qualifier
...
@@ -15,7 +16,8 @@ ERROR: 0:42: 'location qualifier on output' : not supported in this stage: verte
...
@@ -15,7 +16,8 @@ ERROR: 0:42: 'location qualifier on output' : not supported in this stage: verte
ERROR: 0:50: 'shared' : not supported with this profile: es
ERROR: 0:50: 'shared' : not supported with this profile: es
ERROR: 0:50: 'shared' : not supported in this stage: vertex
ERROR: 0:50: 'shared' : not supported in this stage: vertex
ERROR: 0:54: 'aoeuntaoeu' : layout qualifiers for matrix layout and packing only apply to uniform or buffer blocks
ERROR: 0:54: 'aoeuntaoeu' : layout qualifiers for matrix layout and packing only apply to uniform or buffer blocks
ERROR: 16 compilation errors. No code generated.
ERROR: 0:57: 'location' : repeated use of location 40
ERROR: 18 compilation errors. No code generated.
ERROR: node is still EOpNull!
ERROR: node is still EOpNull!
...
@@ -72,6 +74,8 @@ ERROR: node is still EOpNull!
...
@@ -72,6 +74,8 @@ ERROR: node is still EOpNull!
0:? 'badoutA' (layout(location=10 ) smooth out highp 4-component vector of float)
0:? 'badoutA' (layout(location=10 ) smooth out highp 4-component vector of float)
0:? 'compute_only' (shared highp 4-component vector of float)
0:? 'compute_only' (shared highp 4-component vector of float)
0:? 'aoeuntaoeu' (layout(packed ) highp float)
0:? 'aoeuntaoeu' (layout(packed ) highp float)
0:? 'cd' (layout(location=40 ) in highp float)
0:? 'ce' (layout(location=37 ) in highp 4X3 matrix of float)
0:? 'gl_VertexID' (gl_VertexId highp int)
0:? 'gl_VertexID' (gl_VertexId highp int)
0:? 'gl_InstanceID' (gl_InstanceId highp int)
0:? 'gl_InstanceID' (gl_InstanceId highp int)
...
...
Test/baseResults/400.frag.out
View file @
0b391370
...
@@ -197,7 +197,7 @@ ERROR: node is still EOpNull!
...
@@ -197,7 +197,7 @@ ERROR: node is still EOpNull!
0:? 'isamp2DA' (uniform isampler2DArray)
0:? 'isamp2DA' (uniform isampler2DArray)
0:? 'gl_ClipDistance' (smooth in 4-element array of float)
0:? 'gl_ClipDistance' (smooth in 4-element array of float)
0:? 'vl' (layout(location=4 ) smooth in 4-component vector of float)
0:? 'vl' (layout(location=4 ) smooth in 4-component vector of float)
0:? 'vl2' (layout(location=
4
) smooth in 4-component vector of float)
0:? 'vl2' (layout(location=
6
) smooth in 4-component vector of float)
0:? 'uv3' (layout(location=3 ) uniform 3-component vector of float)
0:? 'uv3' (layout(location=3 ) uniform 3-component vector of float)
0:? '__anon__0' (in block{gl_FogFragCoord,gl_TexCoord,gl_Color,gl_SecondaryColor})
0:? '__anon__0' (in block{gl_FogFragCoord,gl_TexCoord,gl_Color,gl_SecondaryColor})
0:? '__anon__0' (in block{gl_FogFragCoord,gl_TexCoord,gl_Color,gl_SecondaryColor})
0:? '__anon__0' (in block{gl_FogFragCoord,gl_TexCoord,gl_Color,gl_SecondaryColor})
...
...
Test/baseResults/430.vert.out
View file @
0b391370
...
@@ -11,7 +11,8 @@ ERROR: 0:25: 'm3' : cannot use layout qualifiers on structure members
...
@@ -11,7 +11,8 @@ ERROR: 0:25: 'm3' : cannot use layout qualifiers on structure members
ERROR: 0:28: '' : cannot use invariant qualifier on a function parameter
ERROR: 0:28: '' : cannot use invariant qualifier on a function parameter
ERROR: 0:30: '' : cannot use layout qualifiers on a function parameter
ERROR: 0:30: '' : cannot use layout qualifiers on a function parameter
ERROR: 0:31: '' : cannot use auxiliary or interpolation qualifiers on a function parameter
ERROR: 0:31: '' : cannot use auxiliary or interpolation qualifiers on a function parameter
ERROR: 11 compilation errors. No code generated.
ERROR: 0:42: 'location' : repeated use of location 53
ERROR: 12 compilation errors. No code generated.
ERROR: node is still EOpNull!
ERROR: node is still EOpNull!
...
@@ -36,10 +37,13 @@ ERROR: node is still EOpNull!
...
@@ -36,10 +37,13 @@ ERROR: node is still EOpNull!
0:31 'cv3' (in 3-component vector of float)
0:31 'cv3' (in 3-component vector of float)
0:? Linker Objects
0:? Linker Objects
0:? 'v4' (layout(location=3 ) 4-component vector of float)
0:? 'v4' (layout(location=3 ) 4-component vector of float)
0:? 'uv4' (layout(location=
3
) uniform 4-component vector of float)
0:? 'uv4' (layout(location=
4
) uniform 4-component vector of float)
0:? 'b1' (layout(location=2 ) in block{v})
0:? 'b1' (layout(location=2 ) in block{v})
0:? 'b2' (layout(location=2 ) out block{v})
0:? 'b2' (layout(location=2 ) out block{v})
0:? '__anon__0' (out block{gl_ClipDistance})
0:? '__anon__0' (out block{gl_ClipDistance})
0:? 'cs' (layout(location=10 ) smooth out 2-element array of structure{m,f})
0:? 'cf' (layout(location=54 ) smooth out float)
0:? 'cg' (layout(location=53 ) smooth out float)
0:? 'gl_VertexID' (gl_VertexId int)
0:? 'gl_VertexID' (gl_VertexId int)
0:? 'gl_InstanceID' (gl_InstanceId int)
0:? 'gl_InstanceID' (gl_InstanceId int)
...
...
Test/baseResults/specExamples.frag.out
View file @
0b391370
...
@@ -16,6 +16,7 @@ ERROR: 0:99: 'local_size_y' : there is no such layout identifier for this stage
...
@@ -16,6 +16,7 @@ ERROR: 0:99: 'local_size_y' : there is no such layout identifier for this stage
ERROR: 0:100: 'local_size_x' : there is no such layout identifier for this stage taking an assigned value
ERROR: 0:100: 'local_size_x' : there is no such layout identifier for this stage taking an assigned value
ERROR: 0:102: 'color' : redefinition
ERROR: 0:102: 'color' : redefinition
ERROR: 0:103: 'index' : there is no such layout identifier for this stage taking an assigned value
ERROR: 0:103: 'index' : there is no such layout identifier for this stage taking an assigned value
ERROR: 0:104: 'location' : repeated use of location 3
ERROR: 0:106: 'depth_greater' : unrecognized layout identifier, or qualifier requires assignemnt (e.g., binding = 4)
ERROR: 0:106: 'depth_greater' : unrecognized layout identifier, or qualifier requires assignemnt (e.g., binding = 4)
ERROR: 0:112: 'depth_any' : unrecognized layout identifier, or qualifier requires assignemnt (e.g., binding = 4)
ERROR: 0:112: 'depth_any' : unrecognized layout identifier, or qualifier requires assignemnt (e.g., binding = 4)
ERROR: 0:115: 'depth_greater' : unrecognized layout identifier, or qualifier requires assignemnt (e.g., binding = 4)
ERROR: 0:115: 'depth_greater' : unrecognized layout identifier, or qualifier requires assignemnt (e.g., binding = 4)
...
@@ -48,7 +49,7 @@ ERROR: 0:226: 'in' : not allowed in nested scope
...
@@ -48,7 +49,7 @@ ERROR: 0:226: 'in' : not allowed in nested scope
ERROR: 0:227: 'in' : not allowed in nested scope
ERROR: 0:227: 'in' : not allowed in nested scope
ERROR: 0:228: 'in' : not allowed in nested scope
ERROR: 0:228: 'in' : not allowed in nested scope
ERROR: 0:232: 'out' : not allowed in nested scope
ERROR: 0:232: 'out' : not allowed in nested scope
ERROR: 4
8
compilation errors. No code generated.
ERROR: 4
9
compilation errors. No code generated.
gl_FragCoord pixel center is integer
gl_FragCoord pixel center is integer
...
...
glslang/Include/revision.h
View file @
0b391370
...
@@ -9,5 +9,5 @@
...
@@ -9,5 +9,5 @@
// source have to figure out how to create revision.h just to get a build
// source have to figure out how to create revision.h just to get a build
// going. However, if it is not updated, it can be a version behind.
// going. However, if it is not updated, it can be a version behind.
#define GLSLANG_REVISION "2435
5
"
#define GLSLANG_REVISION "2435
6
"
#define GLSLANG_DATE "2013/12/04 14:
48:20
"
#define GLSLANG_DATE "2013/12/04 14:
50:38
"
glslang/MachineIndependent/ParseHelper.cpp
View file @
0b391370
...
@@ -2649,6 +2649,10 @@ void TParseContext::layoutTypeCheck(TSourceLoc loc, const TSymbol& symbol)
...
@@ -2649,6 +2649,10 @@ void TParseContext::layoutTypeCheck(TSourceLoc loc, const TSymbol& symbol)
default
:
default
:
break
;
break
;
}
}
int
repeated
=
intermediate
.
addUsedLocation
(
qualifier
,
type
);
if
(
repeated
>=
0
)
error
(
loc
,
"repeated use of location"
,
"location"
,
"%d"
,
repeated
);
}
}
if
(
qualifier
.
hasBinding
())
{
if
(
qualifier
.
hasBinding
())
{
...
...
glslang/MachineIndependent/linkValidate.cpp
View file @
0b391370
...
@@ -443,4 +443,95 @@ bool TIntermediate::userOutputUsed() const
...
@@ -443,4 +443,95 @@ bool TIntermediate::userOutputUsed() const
return
found
;
return
found
;
}
}
// Accumulate locations used for inputs, outputs, and uniforms, and check for collisions
// as the accumulation is done.
//
// Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value.
int
TIntermediate
::
addUsedLocation
(
const
TQualifier
&
qualifier
,
const
TType
&
type
)
{
int
set
;
if
(
qualifier
.
isPipeInput
())
set
=
0
;
else
if
(
qualifier
.
isPipeOutput
())
set
=
1
;
else
if
(
qualifier
.
isUniform
())
set
=
2
;
else
return
-
1
;
int
size
;
if
(
qualifier
.
isUniform
())
{
if
(
type
.
isArray
())
size
=
type
.
getArraySize
();
else
size
=
1
;
}
else
size
=
computeTypeLocationSize
(
type
);
TRange
range
=
{
qualifier
.
layoutSlotLocation
,
qualifier
.
layoutSlotLocation
+
size
-
1
};
// check for collisions
for
(
size_t
r
=
0
;
r
<
usedLocations
[
set
].
size
();
++
r
)
{
if
(
range
.
last
>=
usedLocations
[
set
][
r
].
start
&&
range
.
start
<=
usedLocations
[
set
][
r
].
last
)
{
// there is a collision; pick one
return
std
::
max
(
range
.
start
,
usedLocations
[
set
][
r
].
start
);
}
}
usedLocations
[
set
].
push_back
(
range
);
return
-
1
;
}
// Recursively figure out how many locations are used up by an input or output type.
// Return the size of type, as measured by "locations".
int
TIntermediate
::
computeTypeLocationSize
(
const
TType
&
type
)
{
// "If the declared input is an array of size n and each element takes m locations, it will be assigned m * n
// consecutive locations..."
if
(
type
.
isArray
())
{
TType
elementType
(
type
,
0
);
return
type
.
getArraySize
()
*
computeTypeLocationSize
(
elementType
);
}
// "The locations consumed by block and structure members are determined by applying the rules above
// recursively..."
if
(
type
.
isStruct
())
{
// TODO: 440 functionality: input/output block locations when members also have locations
int
size
=
0
;
for
(
size_t
member
=
0
;
member
<
type
.
getStruct
()
->
size
();
++
member
)
{
TType
memberType
(
type
,
member
);
size
+=
computeTypeLocationSize
(
memberType
);
}
return
size
;
}
// "If a vertex shader input is any scalar or vector type, it will consume a single location. If a non-vertex
// shader input is a scalar or vector type other than dvec3 or dvec4, it will consume a single location, while
// types dvec3 or dvec4 will consume two consecutive locations. Inputs of type double and dvec2 will
// consume only a single location, in all stages."
if
(
type
.
isScalar
())
return
1
;
if
(
type
.
isVector
())
{
if
(
language
==
EShLangVertex
&&
type
.
getQualifier
().
isPipeInput
())
return
1
;
if
(
type
.
getBasicType
()
==
EbtDouble
&&
type
.
getVectorSize
()
>
2
)
return
2
;
else
return
1
;
}
// "If the declared input is an n x m single- or double-precision matrix, ...
// The number of locations assigned for each matrix will be the same as
// for an n-element array of m-component vectors..."
if
(
type
.
isMatrix
())
{
TType
columnType
(
type
,
0
);
return
type
.
getMatrixCols
()
*
computeTypeLocationSize
(
columnType
);
}
assert
(
0
);
return
1
;
}
}
// end namespace glslang
}
// end namespace glslang
glslang/MachineIndependent/localintermediate.h
View file @
0b391370
...
@@ -155,6 +155,8 @@ public:
...
@@ -155,6 +155,8 @@ public:
void
addIoAccessed
(
const
TString
&
name
)
{
ioAccessed
.
insert
(
name
);
}
void
addIoAccessed
(
const
TString
&
name
)
{
ioAccessed
.
insert
(
name
);
}
bool
inIoAccessed
(
const
TString
&
name
)
const
{
return
ioAccessed
.
find
(
name
)
!=
ioAccessed
.
end
();
}
bool
inIoAccessed
(
const
TString
&
name
)
const
{
return
ioAccessed
.
find
(
name
)
!=
ioAccessed
.
end
();
}
int
addUsedLocation
(
const
TQualifier
&
,
const
TType
&
);
protected
:
protected
:
void
error
(
TInfoSink
&
infoSink
,
const
char
*
);
void
error
(
TInfoSink
&
infoSink
,
const
char
*
);
void
mergeBodies
(
TInfoSink
&
,
TIntermSequence
&
globals
,
const
TIntermSequence
&
unitGlobals
);
void
mergeBodies
(
TInfoSink
&
,
TIntermSequence
&
globals
,
const
TIntermSequence
&
unitGlobals
);
...
@@ -164,6 +166,7 @@ protected:
...
@@ -164,6 +166,7 @@ protected:
void
inOutLocationCheck
(
TInfoSink
&
);
void
inOutLocationCheck
(
TInfoSink
&
);
TIntermSequence
&
findLinkerObjects
()
const
;
TIntermSequence
&
findLinkerObjects
()
const
;
bool
userOutputUsed
()
const
;
bool
userOutputUsed
()
const
;
int
computeTypeLocationSize
(
const
TType
&
);
protected
:
protected
:
const
EShLanguage
language
;
const
EShLanguage
language
;
...
@@ -194,6 +197,12 @@ protected:
...
@@ -194,6 +197,12 @@ protected:
std
::
set
<
TString
>
ioAccessed
;
// set of names of statically read/written I/O that might need extra checking
std
::
set
<
TString
>
ioAccessed
;
// set of names of statically read/written I/O that might need extra checking
struct
TRange
{
int
start
;
int
last
;
};
std
::
vector
<
TRange
>
usedLocations
[
3
];
// sets of used locations, one for each of in, out, and uniform
private
:
private
:
void
operator
=
(
TIntermediate
&
);
// prevent assignments
void
operator
=
(
TIntermediate
&
);
// prevent assignments
};
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment