Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-winehq
Project
Project
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
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wine
wine-winehq
Commits
cbc86300
Commit
cbc86300
authored
Mar 10, 2009
by
Henri Verbeet
Committed by
Alexandre Julliard
Mar 10, 2009
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
d3d8: Make the shader handle table a bit more generic.
parent
954c3e22
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
108 additions
and
83 deletions
+108
-83
d3d8_private.h
dlls/d3d8/d3d8_private.h
+11
-8
device.c
dlls/d3d8/device.c
+94
-73
directx.c
dlls/d3d8/directx.c
+3
-2
No files found.
dlls/d3d8/d3d8_private.h
View file @
cbc86300
...
...
@@ -35,9 +35,6 @@
#include "d3d8.h"
#include "wine/wined3d.h"
/* Device caps */
#define INITIAL_SHADER_HANDLE_TABLE_SIZE 64
/* CreateVertexShader can return > 0xFFFF */
#define VS_HIGHESTFIXEDFXF 0xF0000000
...
...
@@ -166,7 +163,16 @@ extern const IWineD3DDeviceParentVtbl d3d8_wined3d_device_parent_vtbl;
* IDirect3DDevice8 implementation structure
*/
typedef
void
*
shader_handle
;
#define D3D8_INITIAL_HANDLE_TABLE_SIZE 64
#define D3D8_INVALID_HANDLE ~0U
struct
d3d8_handle_table
{
void
**
entries
;
void
**
free_entries
;
UINT
table_size
;
UINT
entry_count
;
};
struct
FvfToDecl
{
...
...
@@ -182,10 +188,7 @@ struct IDirect3DDevice8Impl
LONG
ref
;
/* But what about baseVertexIndex in state blocks? hmm... it may be a better idea to pass this to wined3d */
IWineD3DDevice
*
WineD3DDevice
;
DWORD
shader_handle_table_size
;
DWORD
allocated_shader_handles
;
shader_handle
*
shader_handles
;
shader_handle
*
free_shader_handles
;
struct
d3d8_handle_table
handle_table
;
/* FVF management */
struct
FvfToDecl
*
decls
;
...
...
dlls/d3d8/device.c
View file @
cbc86300
...
...
@@ -162,29 +162,50 @@ static UINT vertex_count_from_primitive_count(D3DPRIMITIVETYPE primitive_type, U
}
}
/* Shader handle functions */
static
shader_handle
*
alloc_shader_handle
(
IDirect3DDevice8Impl
*
This
)
{
if
(
This
->
free_shader_handles
)
{
/* Handle table functions */
static
DWORD
d3d8_allocate_handle
(
struct
d3d8_handle_table
*
t
,
void
*
object
)
{
if
(
t
->
free_entries
)
{
/* Use a free handle */
shader_handle
*
handle
=
This
->
free_shader_handles
;
This
->
free_shader_handles
=
*
handle
;
return
handle
;
void
**
entry
=
t
->
free_entries
;
t
->
free_entries
=
*
entry
;
*
entry
=
object
;
return
entry
-
t
->
entries
;
}
if
(
!
(
This
->
allocated_shader_handles
<
This
->
shader_handle_table_size
))
{
if
(
!
(
t
->
entry_count
<
t
->
table_size
))
{
/* Grow the table */
DWORD
new_size
=
This
->
shader_handle_table_size
+
(
This
->
shader_handle_
table_size
>>
1
);
shader_handle
*
new_handles
=
HeapReAlloc
(
GetProcessHeap
(),
0
,
This
->
shader_handles
,
new_size
*
sizeof
(
shader_handle
));
if
(
!
new_
handles
)
return
NULL
;
This
->
shader_handles
=
new_handl
es
;
This
->
shader_handle_
table_size
=
new_size
;
UINT
new_size
=
t
->
table_size
+
(
t
->
table_size
>>
1
);
void
**
new_entries
=
HeapReAlloc
(
GetProcessHeap
(),
0
,
t
->
entries
,
new_size
*
sizeof
(
void
*
));
if
(
!
new_
entries
)
return
D3D8_INVALID_HANDLE
;
t
->
entries
=
new_entri
es
;
t
->
table_size
=
new_size
;
}
return
&
This
->
shader_handles
[
This
->
allocated_shader_handles
++
];
t
->
entries
[
t
->
entry_count
]
=
object
;
return
t
->
entry_count
++
;
}
static
void
free_shader_handle
(
IDirect3DDevice8Impl
*
This
,
shader_handle
*
handle
)
{
*
handle
=
This
->
free_shader_handles
;
This
->
free_shader_handles
=
handle
;
static
void
*
d3d8_free_handle
(
struct
d3d8_handle_table
*
t
,
DWORD
handle
)
{
void
**
entry
,
*
object
;
if
(
handle
>=
t
->
entry_count
)
return
NULL
;
entry
=
&
t
->
entries
[
handle
];
object
=
*
entry
;
*
entry
=
t
->
free_entries
;
t
->
free_entries
=
entry
;
return
object
;
}
static
void
*
d3d8_get_object
(
struct
d3d8_handle_table
*
t
,
DWORD
handle
)
{
if
(
handle
>=
t
->
entry_count
)
return
NULL
;
return
t
->
entries
[
handle
];
}
/* IDirect3D IUnknown parts follow: */
...
...
@@ -243,7 +264,7 @@ static ULONG WINAPI IDirect3DDevice8Impl_Release(LPDIRECT3DDEVICE8 iface) {
IWineD3DDevice_Uninit3D
(
This
->
WineD3DDevice
,
D3D8CB_DestroyDepthStencilSurface
,
D3D8CB_DestroySwapChain
);
IWineD3DDevice_Release
(
This
->
WineD3DDevice
);
HeapFree
(
GetProcessHeap
(),
0
,
This
->
shader_handl
es
);
HeapFree
(
GetProcessHeap
(),
0
,
This
->
handle_table
.
entri
es
);
HeapFree
(
GetProcessHeap
(),
0
,
This
);
LeaveCriticalSection
(
&
d3d8_cs
);
}
...
...
@@ -1661,7 +1682,7 @@ static HRESULT WINAPI IDirect3DDevice8Impl_CreateVertexShader(LPDIRECT3DDEVICE8
IDirect3DVertexShader8Impl
*
object
;
IWineD3DVertexDeclaration
*
wined3d_vertex_declaration
;
const
DWORD
*
token
=
pDeclaration
;
shader_handle
*
handle
;
DWORD
handle
;
/* Test if the vertex declaration is valid */
while
(
D3DVSD_END
()
!=
*
token
)
{
...
...
@@ -1701,8 +1722,8 @@ static HRESULT WINAPI IDirect3DDevice8Impl_CreateVertexShader(LPDIRECT3DDEVICE8
return
D3DERR_INVALIDCALL
;
}
handle
=
alloc_shader_handle
(
This
);
if
(
!
handle
)
handle
=
d3d8_allocate_handle
(
&
This
->
handle_table
,
object
);
if
(
handle
==
D3D8_INVALID_HANDLE
)
{
ERR
(
"Failed to allocate shader handle
\n
"
);
LeaveCriticalSection
(
&
d3d8_cs
);
...
...
@@ -1713,8 +1734,7 @@ static HRESULT WINAPI IDirect3DDevice8Impl_CreateVertexShader(LPDIRECT3DDEVICE8
}
else
{
DWORD
shader_handle
=
(
handle
-
This
->
shader_handles
)
+
VS_HIGHESTFIXEDFXF
+
1
;
*
handle
=
object
;
DWORD
shader_handle
=
handle
+
VS_HIGHESTFIXEDFXF
+
1
;
*
ppShader
=
((
IDirect3DVertexDeclaration8Impl
*
)
object
->
vertex_declaration
)
->
shader_handle
=
shader_handle
;
}
...
...
@@ -1730,7 +1750,7 @@ static HRESULT WINAPI IDirect3DDevice8Impl_CreateVertexShader(LPDIRECT3DDEVICE8
{
/* free up object */
FIXME
(
"Call to IWineD3DDevice_CreateVertexShader failed
\n
"
);
free_shader_handle
(
This
,
handle
);
d3d8_free_handle
(
&
This
->
handle_table
,
handle
);
IDirect3DVertexDeclaration8_Release
(
object
->
vertex_declaration
);
HeapFree
(
GetProcessHeap
(),
0
,
object
);
*
ppShader
=
0
;
...
...
@@ -1828,25 +1848,22 @@ static HRESULT WINAPI IDirect3DDevice8Impl_SetVertexShader(LPDIRECT3DDEVICE8 ifa
IDirect3DDevice8Impl_FindDecl
(
This
,
pShader
)
->
wined3d_vertex_declaration
);
IWineD3DDevice_SetVertexShader
(
This
->
WineD3DDevice
,
NULL
);
}
else
{
IDirect3DVertexShader8Impl
*
shader
;
TRACE
(
"Setting shader
\n
"
);
if
(
This
->
allocated_shader_handles
<=
pShader
-
(
VS_HIGHESTFIXEDFXF
+
1
))
{
FIXME
(
"(%p) : Number of shaders exceeds the maximum number of possible shaders
\n
"
,
This
);
hrc
=
D3DERR_INVALIDCALL
;
}
else
{
IDirect3DVertexShader8Impl
*
shader
=
This
->
shader_handles
[
pShader
-
(
VS_HIGHESTFIXEDFXF
+
1
)];
if
(
shader
)
{
hrc
=
IWineD3DDevice_SetVertexDeclaration
(
This
->
WineD3DDevice
,
((
IDirect3DVertexDeclaration8Impl
*
)
shader
->
vertex_declaration
)
->
wined3d_vertex_declaration
);
if
(
SUCCEEDED
(
hrc
))
hrc
=
IWineD3DDevice_SetVertexShader
(
This
->
WineD3DDevice
,
shader
->
wineD3DVertexShader
);
}
else
{
hrc
=
IWineD3DDevice_SetVertexDeclaration
(
This
->
WineD3DDevice
,
NULL
);
if
(
SUCCEEDED
(
hrc
))
hrc
=
IWineD3DDevice_SetVertexShader
(
This
->
WineD3DDevice
,
NULL
);
}
shader
=
d3d8_get_object
(
&
This
->
handle_table
,
pShader
-
(
VS_HIGHESTFIXEDFXF
+
1
));
if
(
!
shader
)
{
WARN
(
"Invalid handle (%#x) passed.
\n
"
,
pShader
);
hrc
=
D3DERR_INVALIDCALL
;
}
else
{
hrc
=
IWineD3DDevice_SetVertexDeclaration
(
This
->
WineD3DDevice
,
((
IDirect3DVertexDeclaration8Impl
*
)
shader
->
vertex_declaration
)
->
wined3d_vertex_declaration
);
if
(
SUCCEEDED
(
hrc
))
hrc
=
IWineD3DDevice_SetVertexShader
(
This
->
WineD3DDevice
,
shader
->
wineD3DVertexShader
);
}
}
TRACE
(
"(%p) : returning hr(%u)
\n
"
,
This
,
hrc
);
...
...
@@ -1896,18 +1913,20 @@ static HRESULT WINAPI IDirect3DDevice8Impl_GetVertexShader(LPDIRECT3DDEVICE8 ifa
static
HRESULT
WINAPI
IDirect3DDevice8Impl_DeleteVertexShader
(
LPDIRECT3DDEVICE8
iface
,
DWORD
pShader
)
{
IDirect3DDevice8Impl
*
This
=
(
IDirect3DDevice8Impl
*
)
iface
;
IDirect3DVertexShader8Impl
*
shader
;
TRACE
(
"(%p) : pShader %#x
\n
"
,
This
,
pShader
);
EnterCriticalSection
(
&
d3d8_cs
);
if
(
pShader
<=
VS_HIGHESTFIXEDFXF
||
This
->
allocated_shader_handles
<=
pShader
-
(
VS_HIGHESTFIXEDFXF
+
1
))
{
ERR
(
"(%p) : Trying to delete an invalid handle
\n
"
,
This
);
shader
=
d3d8_free_handle
(
&
This
->
handle_table
,
pShader
-
(
VS_HIGHESTFIXEDFXF
+
1
));
if
(
!
shader
)
{
WARN
(
"Invalid handle (%#x) passed.
\n
"
,
pShader
);
LeaveCriticalSection
(
&
d3d8_cs
);
return
D3DERR_INVALIDCALL
;
}
else
{
IWineD3DVertexShader
*
cur
=
NULL
;
shader_handle
*
handle
=
&
This
->
shader_handles
[
pShader
-
(
VS_HIGHESTFIXEDFXF
+
1
)];
IDirect3DVertexShader8Impl
*
shader
=
*
handle
;
IWineD3DDevice_GetVertexShader
(
This
->
WineD3DDevice
,
&
cur
);
if
(
cur
)
{
...
...
@@ -1919,8 +1938,6 @@ static HRESULT WINAPI IDirect3DDevice8Impl_DeleteVertexShader(LPDIRECT3DDEVICE
{
ERR
(
"Shader %p has references left, this shouldn't happen.
\n
"
,
shader
);
}
free_shader_handle
(
This
,
handle
);
}
LeaveCriticalSection
(
&
d3d8_cs
);
...
...
@@ -1952,18 +1969,19 @@ static HRESULT WINAPI IDirect3DDevice8Impl_GetVertexShaderConstant(LPDIRECT3DDEV
static
HRESULT
WINAPI
IDirect3DDevice8Impl_GetVertexShaderDeclaration
(
LPDIRECT3DDEVICE8
iface
,
DWORD
pVertexShader
,
void
*
pData
,
DWORD
*
pSizeOfData
)
{
IDirect3DDevice8Impl
*
This
=
(
IDirect3DDevice8Impl
*
)
iface
;
IDirect3DVertexDeclaration8Impl
*
declaration
;
IDirect3DVertexShader8Impl
*
shader
=
NULL
;
IDirect3DVertexShader8Impl
*
shader
;
TRACE
(
"(%p) : pVertexShader 0x%08x, pData %p, *pSizeOfData %u
\n
"
,
This
,
pVertexShader
,
pData
,
*
pSizeOfData
);
EnterCriticalSection
(
&
d3d8_cs
);
if
(
pVertexShader
<=
VS_HIGHESTFIXEDFXF
||
This
->
allocated_shader_handles
<=
pVertexShader
-
(
VS_HIGHESTFIXEDFXF
+
1
))
{
ERR
(
"Passed an invalid shader handle.
\n
"
);
shader
=
d3d8_get_object
(
&
This
->
handle_table
,
pVertexShader
-
(
VS_HIGHESTFIXEDFXF
+
1
));
if
(
!
shader
)
{
WARN
(
"Invalid handle (%#x) passed.
\n
"
,
pVertexShader
);
LeaveCriticalSection
(
&
d3d8_cs
);
return
D3DERR_INVALIDCALL
;
}
shader
=
This
->
shader_handles
[
pVertexShader
-
(
VS_HIGHESTFIXEDFXF
+
1
)];
declaration
=
(
IDirect3DVertexDeclaration8Impl
*
)
shader
->
vertex_declaration
;
/* If pData is NULL, we just return the required size of the buffer. */
...
...
@@ -1995,13 +2013,15 @@ static HRESULT WINAPI IDirect3DDevice8Impl_GetVertexShaderFunction(LPDIRECT3DDEV
TRACE
(
"(%p) : pVertexShader %#x, pData %p, pSizeOfData %p
\n
"
,
This
,
pVertexShader
,
pData
,
pSizeOfData
);
EnterCriticalSection
(
&
d3d8_cs
);
if
(
pVertexShader
<=
VS_HIGHESTFIXEDFXF
||
This
->
allocated_shader_handles
<=
pVertexShader
-
(
VS_HIGHESTFIXEDFXF
+
1
))
{
ERR
(
"Passed an invalid shader handle.
\n
"
);
shader
=
d3d8_get_object
(
&
This
->
handle_table
,
pVertexShader
-
(
VS_HIGHESTFIXEDFXF
+
1
));
if
(
!
shader
)
{
WARN
(
"Invalid handle (%#x) passed.
\n
"
,
pVertexShader
);
LeaveCriticalSection
(
&
d3d8_cs
);
return
D3DERR_INVALIDCALL
;
}
shader
=
This
->
shader_handles
[
pVertexShader
-
(
VS_HIGHESTFIXEDFXF
+
1
)];
if
(
shader
->
wineD3DVertexShader
)
{
hr
=
IWineD3DVertexShader_GetFunction
(
shader
->
wineD3DVertexShader
,
pData
,
pSizeOfData
);
...
...
@@ -2087,15 +2107,14 @@ static HRESULT WINAPI IDirect3DDevice8Impl_CreatePixelShader(LPDIRECT3DDEVICE8 i
HeapFree
(
GetProcessHeap
(),
0
,
object
);
*
ppShader
=
0
;
}
else
{
shader_handle
*
handle
=
alloc_shader_handle
(
This
);
if
(
!
handle
)
{
DWORD
handle
=
d3d8_allocate_handle
(
&
This
->
handle_table
,
object
);
if
(
handle
==
D3D8_INVALID_HANDLE
)
{
ERR
(
"Failed to allocate shader handle
\n
"
);
IDirect3DVertexShader8_Release
((
IUnknown
*
)
object
);
hrc
=
E_OUTOFMEMORY
;
}
else
{
*
handle
=
object
;
object
->
handle
=
(
handle
-
This
->
shader_handles
)
+
VS_HIGHESTFIXEDFXF
+
1
;
*
ppShader
=
object
->
handle
;
*
ppShader
=
object
->
handle
=
handle
+
VS_HIGHESTFIXEDFXF
+
1
;
TRACE
(
"(%p) : returning %p (handle %#x)
\n
"
,
This
,
object
,
*
ppShader
);
}
}
...
...
@@ -2107,16 +2126,17 @@ static HRESULT WINAPI IDirect3DDevice8Impl_CreatePixelShader(LPDIRECT3DDEVICE8 i
static
HRESULT
WINAPI
IDirect3DDevice8Impl_SetPixelShader
(
LPDIRECT3DDEVICE8
iface
,
DWORD
pShader
)
{
IDirect3DDevice8Impl
*
This
=
(
IDirect3DDevice8Impl
*
)
iface
;
IDirect3DPixelShader8Impl
*
shader
=
NULL
;
IDirect3DPixelShader8Impl
*
shader
;
HRESULT
hr
;
TRACE
(
"(%p) : pShader %#x
\n
"
,
This
,
pShader
);
EnterCriticalSection
(
&
d3d8_cs
);
if
(
pShader
>
VS_HIGHESTFIXEDFXF
&&
This
->
allocated_shader_handles
>
pShader
-
(
VS_HIGHESTFIXEDFXF
+
1
))
{
shader
=
This
->
shader_handles
[
pShader
-
(
VS_HIGHESTFIXEDFXF
+
1
)];
}
else
if
(
pShader
)
{
ERR
(
"Trying to set an invalid handle.
\n
"
);
shader
=
d3d8_get_object
(
&
This
->
handle_table
,
pShader
-
(
VS_HIGHESTFIXEDFXF
+
1
));
if
(
!
shader
)
{
WARN
(
"Invalid handle (%#x) passed.
\n
"
,
pShader
);
}
TRACE
(
"(%p) : Setting shader %p
\n
"
,
This
,
shader
);
...
...
@@ -2154,18 +2174,20 @@ static HRESULT WINAPI IDirect3DDevice8Impl_GetPixelShader(LPDIRECT3DDEVICE8 ifac
static
HRESULT
WINAPI
IDirect3DDevice8Impl_DeletePixelShader
(
LPDIRECT3DDEVICE8
iface
,
DWORD
pShader
)
{
IDirect3DDevice8Impl
*
This
=
(
IDirect3DDevice8Impl
*
)
iface
;
IDirect3DPixelShader8Impl
*
shader
;
TRACE
(
"(%p) : pShader %#x
\n
"
,
This
,
pShader
);
EnterCriticalSection
(
&
d3d8_cs
);
if
(
pShader
<=
VS_HIGHESTFIXEDFXF
||
This
->
allocated_shader_handles
<=
pShader
-
(
VS_HIGHESTFIXEDFXF
+
1
))
{
ERR
(
"(%p) : Trying to delete an invalid handle
\n
"
,
This
);
shader
=
d3d8_free_handle
(
&
This
->
handle_table
,
pShader
-
(
VS_HIGHESTFIXEDFXF
+
1
));
if
(
!
shader
)
{
WARN
(
"Invalid handle (%#x) passed.
\n
"
,
pShader
);
LeaveCriticalSection
(
&
d3d8_cs
);
return
D3DERR_INVALIDCALL
;
}
else
{
IWineD3DPixelShader
*
cur
=
NULL
;
shader_handle
*
handle
=
&
This
->
shader_handles
[
pShader
-
(
VS_HIGHESTFIXEDFXF
+
1
)];
IDirect3DPixelShader8Impl
*
shader
=
*
handle
;
IWineD3DDevice_GetPixelShader
(
This
->
WineD3DDevice
,
&
cur
);
if
(
cur
)
{
...
...
@@ -2177,8 +2199,6 @@ static HRESULT WINAPI IDirect3DDevice8Impl_DeletePixelShader(LPDIRECT3DDEVICE8 i
{
ERR
(
"Shader %p has references left, this shouldn't happen.
\n
"
,
shader
);
}
free_shader_handle
(
This
,
handle
);
}
LeaveCriticalSection
(
&
d3d8_cs
);
...
...
@@ -2215,13 +2235,14 @@ static HRESULT WINAPI IDirect3DDevice8Impl_GetPixelShaderFunction(LPDIRECT3DDEVI
TRACE
(
"(%p) : pPixelShader %#x, pData %p, pSizeOfData %p
\n
"
,
This
,
pPixelShader
,
pData
,
pSizeOfData
);
EnterCriticalSection
(
&
d3d8_cs
);
if
(
pPixelShader
<=
VS_HIGHESTFIXEDFXF
||
This
->
allocated_shader_handles
<=
pPixelShader
-
(
VS_HIGHESTFIXEDFXF
+
1
))
{
ERR
(
"Passed an invalid shader handle.
\n
"
);
shader
=
d3d8_get_object
(
&
This
->
handle_table
,
pPixelShader
-
(
VS_HIGHESTFIXEDFXF
+
1
));
if
(
!
shader
)
{
WARN
(
"Invalid handle (%#x) passed.
\n
"
,
pPixelShader
);
LeaveCriticalSection
(
&
d3d8_cs
);
return
D3DERR_INVALIDCALL
;
}
shader
=
This
->
shader_handles
[
pPixelShader
-
(
VS_HIGHESTFIXEDFXF
+
1
)];
hr
=
IWineD3DPixelShader_GetFunction
(
shader
->
wineD3DPixelShader
,
pData
,
pSizeOfData
);
LeaveCriticalSection
(
&
d3d8_cs
);
return
hr
;
...
...
dlls/d3d8/directx.c
View file @
cbc86300
...
...
@@ -323,8 +323,9 @@ static HRESULT WINAPI IDirect3D8Impl_CreateDevice(LPDIRECT3D8 iface, UINT Adapte
object
->
lpVtbl
=
&
Direct3DDevice8_Vtbl
;
object
->
device_parent_vtbl
=
&
d3d8_wined3d_device_parent_vtbl
;
object
->
ref
=
1
;
object
->
shader_handles
=
HeapAlloc
(
GetProcessHeap
(),
HEAP_ZERO_MEMORY
,
INITIAL_SHADER_HANDLE_TABLE_SIZE
*
sizeof
(
shader_handle
));
object
->
shader_handle_table_size
=
INITIAL_SHADER_HANDLE_TABLE_SIZE
;
object
->
handle_table
.
entries
=
HeapAlloc
(
GetProcessHeap
(),
HEAP_ZERO_MEMORY
,
D3D8_INITIAL_HANDLE_TABLE_SIZE
*
sizeof
(
*
object
->
handle_table
.
entries
));
object
->
handle_table
.
table_size
=
D3D8_INITIAL_HANDLE_TABLE_SIZE
;
*
ppReturnedDeviceInterface
=
(
IDirect3DDevice8
*
)
object
;
/* Allocate an associated WineD3DDevice object */
...
...
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