Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
skiko
Project
Project
Details
Activity
Releases
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
liuqiang
skiko
Commits
f1b93ac7
Commit
f1b93ac7
authored
Apr 27, 2021
by
Roman Sedaikin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Reinit render target/surface/canvas only when the SkiaLayer is resized (DirectX).
parent
0158424b
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
325 additions
and
218 deletions
+325
-218
App.kt
...kijaInjectSample/src/main/kotlin/SkijaInjectSample/App.kt
+25
-5
directXRedrawer.cc
skiko/src/jvmMain/cpp/windows/directXRedrawer.cc
+242
-193
ContextHandler.kt
...Main/kotlin/org/jetbrains/skiko/context/ContextHandler.kt
+1
-1
Direct3DContextHandler.kt
...lin/org/jetbrains/skiko/context/Direct3DContextHandler.kt
+49
-14
Direct3DRedrawer.kt
...n/kotlin/org/jetbrains/skiko/redrawer/Direct3DRedrawer.kt
+8
-5
No files found.
samples/SkijaInjectSample/src/main/kotlin/SkijaInjectSample/App.kt
View file @
f1b93ac7
...
@@ -90,11 +90,31 @@ class Renderer(
...
@@ -90,11 +90,31 @@ class Renderer(
var
canvas
:
Canvas
?
=
null
var
canvas
:
Canvas
?
=
null
override
fun
onRender
(
canvas
:
Canvas
,
width
:
Int
,
height
:
Int
,
nanoTime
:
Long
)
{
override
fun
onRender
(
canvas
:
Canvas
,
width
:
Int
,
height
:
Int
,
nanoTime
:
Long
)
{
this
.
canvas
=
canvas
// this.canvas = canvas
val
contentScale
=
layer
.
contentScale
// val contentScale = layer.contentScale
canvas
.
scale
(
contentScale
,
contentScale
)
// canvas.scale(contentScale, contentScale)
displayScene
(
this
,
(
width
/
contentScale
).
toInt
(),
(
height
/
contentScale
).
toInt
(),
nanoTime
)
// displayScene(this, (width / contentScale).toInt(), (height / contentScale).toInt(), nanoTime)
layer
.
needRedraw
()
// layer.needRedraw()
//////////////////////////////////////////////////
val
pictureRecorder
=
PictureRecorder
()
val
pictureCanvas
=
pictureRecorder
.
beginRecording
(
Rect
.
makeLTRB
(
0f
,
0f
,
100f
,
100f
)
)
pictureCanvas
.
drawLine
(
0f
,
0f
,
100f
,
100f
,
Paint
())
val
picture
=
pictureRecorder
.
finishRecordingAsPicture
()
canvas
.
drawPicture
(
picture
,
null
,
Paint
()
// if we replace it by null, everything will be normal
)
canvas
.
drawLine
(
100f
,
0f
,
0f
,
100f
,
Paint
())
//////////////////////////////////////////////////
}
}
}
}
...
...
skiko/src/jvmMain/cpp/windows/directXRedrawer.cc
View file @
f1b93ac7
...
@@ -33,32 +33,27 @@ const int BuffersCount = 2;
...
@@ -33,32 +33,27 @@ const int BuffersCount = 2;
// with the current Swing/Skia integration.
// with the current Swing/Skia integration.
// If PC has other graphics cards suitable for DirectX12, one of them will be used. Otherwise,
// If PC has other graphics cards suitable for DirectX12, one of them will be used. Otherwise,
// rendering will falls back to OpenGL.
// rendering will falls back to OpenGL.
const
std
::
vector
<
std
::
wstring
>
adapterBlacklist
{
const
std
::
vector
<
std
::
wstring
>
adapterBlacklist
{
L"Microsoft Basic Render Driver"
,
L"Microsoft Basic Render Driver"
,
L"Intel(R) HD Graphics 520"
,
L"Intel(R) HD Graphics 520"
,
L"Intel(R) HD Graphics 530"
,
L"Intel(R) HD Graphics 530"
,
L"NVIDIA GeForce GTX 750 Ti"
,
L"NVIDIA GeForce GTX 750 Ti"
,
L"NVIDIA GeForce GTX 960M"
,
L"NVIDIA GeForce GTX 960M"
,
L"NVIDIA Quadro M2000M"
};
L"NVIDIA Quadro M2000M"
};
class
DirectXDevice
class
DirectXDevice
{
{
public
:
public
:
HWND
window
;
GrD3DBackendContext
backendContext
;
GrD3DBackendContext
backendContext
;
gr_cp
<
ID3D12Device
>
device
;
gr_cp
<
ID3D12Device
>
device
;
gr_cp
<
IDXGISwapChain3
>
swapChain
;
gr_cp
<
IDXGISwapChain3
>
swapChain
;
gr_cp
<
ID3D12CommandQueue
>
queue
;
gr_cp
<
ID3D12CommandQueue
>
queue
;
gr_cp
<
ID3D12Resource
>
buffers
[
BuffersCount
];
gr_cp
<
ID3D12Fence
>
fence
;
gr_cp
<
ID3D12Fence
>
fence
;
uint64_t
fenceValues
[
BuffersCount
];
HANDLE
fenceEvent
=
NULL
;
HANDLE
fenceEvent
=
NULL
;
uint64_t
fenceValue
;
unsigned
int
bufferIndex
;
unsigned
int
bufferIndex
=
0
;
unsigned
int
bufferWidth
=
0
;
unsigned
int
bufferHeight
=
0
;
bool
isSizeEqualTo
(
unsigned
int
width
,
unsigned
int
height
)
{
return
width
==
bufferWidth
&&
height
==
bufferHeight
;
}
~
DirectXDevice
()
~
DirectXDevice
()
{
{
...
@@ -66,6 +61,10 @@ public:
...
@@ -66,6 +61,10 @@ public:
{
{
CloseHandle
(
fenceEvent
);
CloseHandle
(
fenceEvent
);
}
}
for
(
int
i
=
0
;
i
<
BuffersCount
;
i
++
)
{
buffers
[
i
].
reset
(
nullptr
);
}
fence
.
reset
(
nullptr
);
fence
.
reset
(
nullptr
);
swapChain
.
reset
(
nullptr
);
swapChain
.
reset
(
nullptr
);
queue
.
reset
(
nullptr
);
queue
.
reset
(
nullptr
);
...
@@ -76,20 +75,20 @@ public:
...
@@ -76,20 +75,20 @@ public:
extern
"C"
extern
"C"
{
{
HRESULT
D3D12CreateDevice
(
HRESULT
D3D12CreateDevice
(
IUnknown
*
pAdapter
,
IUnknown
*
pAdapter
,
D3D_FEATURE_LEVEL
MinimumFeatureLevel
,
D3D_FEATURE_LEVEL
MinimumFeatureLevel
,
REFIID
riid
,
REFIID
riid
,
void
**
ppDevice
void
**
ppDevice
)
)
{
{
typedef
HRESULT
(
*
D3D12CreateDevice_t
)(
typedef
HRESULT
(
*
D3D12CreateDevice_t
)(
IUnknown
*
pAdapter
,
IUnknown
*
pAdapter
,
D3D_FEATURE_LEVEL
MinimumFeatureLevel
,
D3D_FEATURE_LEVEL
MinimumFeatureLevel
,
REFIID
riid
,
REFIID
riid
,
void
**
ppDevice
void
**
ppDevice
);
);
static
D3D12CreateDevice_t
impl
=
nullptr
;
static
D3D12CreateDevice_t
impl
=
nullptr
;
if
(
!
impl
)
{
if
(
!
impl
)
{
auto
d3d12dll
=
LoadLibrary
(
TEXT
(
"D3D12.dll"
));
auto
d3d12dll
=
LoadLibrary
(
TEXT
(
"D3D12.dll"
));
if
(
!
d3d12dll
)
if
(
!
d3d12dll
)
return
E_NOTIMPL
;
return
E_NOTIMPL
;
...
@@ -98,22 +97,22 @@ HRESULT D3D12CreateDevice(
...
@@ -98,22 +97,22 @@ HRESULT D3D12CreateDevice(
return
E_NOTIMPL
;
return
E_NOTIMPL
;
}
}
return
impl
(
pAdapter
,
MinimumFeatureLevel
,
riid
,
ppDevice
);
return
impl
(
pAdapter
,
MinimumFeatureLevel
,
riid
,
ppDevice
);
}
}
HRESULT
D3D12SerializeRootSignature
(
HRESULT
D3D12SerializeRootSignature
(
const
D3D12_ROOT_SIGNATURE_DESC
*
pRootSignature
,
const
D3D12_ROOT_SIGNATURE_DESC
*
pRootSignature
,
D3D_ROOT_SIGNATURE_VERSION
Version
,
D3D_ROOT_SIGNATURE_VERSION
Version
,
ID3DBlob
**
ppBlob
,
ID3DBlob
**
ppBlob
,
ID3DBlob
**
ppErrorBlob
ID3DBlob
**
ppErrorBlob
)
)
{
{
typedef
HRESULT
(
*
D3D12SerializeRootSignature_t
)(
typedef
HRESULT
(
*
D3D12SerializeRootSignature_t
)(
const
D3D12_ROOT_SIGNATURE_DESC
*
pRootSignature
,
const
D3D12_ROOT_SIGNATURE_DESC
*
pRootSignature
,
D3D_ROOT_SIGNATURE_VERSION
Version
,
D3D_ROOT_SIGNATURE_VERSION
Version
,
ID3DBlob
**
ppBlob
,
ID3DBlob
**
ppBlob
,
ID3DBlob
**
ppErrorBlob
ID3DBlob
**
ppErrorBlob
);
);
static
D3D12SerializeRootSignature_t
impl
=
nullptr
;
static
D3D12SerializeRootSignature_t
impl
=
nullptr
;
if
(
!
impl
)
{
if
(
!
impl
)
{
auto
d3d12dll
=
LoadLibrary
(
TEXT
(
"D3D12.dll"
));
auto
d3d12dll
=
LoadLibrary
(
TEXT
(
"D3D12.dll"
));
if
(
!
d3d12dll
)
if
(
!
d3d12dll
)
return
E_NOTIMPL
;
return
E_NOTIMPL
;
...
@@ -122,18 +121,18 @@ HRESULT D3D12SerializeRootSignature(
...
@@ -122,18 +121,18 @@ HRESULT D3D12SerializeRootSignature(
return
E_NOTIMPL
;
return
E_NOTIMPL
;
}
}
return
impl
(
pRootSignature
,
Version
,
ppBlob
,
ppErrorBlob
);
return
impl
(
pRootSignature
,
Version
,
ppBlob
,
ppErrorBlob
);
}
}
HRESULT
CreateDXGIFactory1
(
HRESULT
CreateDXGIFactory1
(
REFIID
riid
,
REFIID
riid
,
void
**
ppFactory
void
**
ppFactory
)
)
{
{
typedef
HRESULT
(
*
CreateDXGIFactory1_t
)(
typedef
HRESULT
(
*
CreateDXGIFactory1_t
)(
REFIID
riid
,
REFIID
riid
,
void
**
ppFactory
void
**
ppFactory
);
);
static
CreateDXGIFactory1_t
impl
=
nullptr
;
static
CreateDXGIFactory1_t
impl
=
nullptr
;
if
(
!
impl
)
{
if
(
!
impl
)
{
auto
dxgidll
=
LoadLibrary
(
TEXT
(
"Dxgi.dll"
));
auto
dxgidll
=
LoadLibrary
(
TEXT
(
"Dxgi.dll"
));
if
(
!
dxgidll
)
if
(
!
dxgidll
)
return
E_NOTIMPL
;
return
E_NOTIMPL
;
...
@@ -142,20 +141,20 @@ HRESULT CreateDXGIFactory1(
...
@@ -142,20 +141,20 @@ HRESULT CreateDXGIFactory1(
return
E_NOTIMPL
;
return
E_NOTIMPL
;
}
}
return
impl
(
riid
,
ppFactory
);
return
impl
(
riid
,
ppFactory
);
}
}
HRESULT
CreateDXGIFactory2
(
HRESULT
CreateDXGIFactory2
(
UINT
Flags
,
UINT
Flags
,
REFIID
riid
,
REFIID
riid
,
void
**
ppFactory
void
**
ppFactory
)
)
{
{
typedef
HRESULT
(
*
CreateDXGIFactory2_t
)(
typedef
HRESULT
(
*
CreateDXGIFactory2_t
)(
UINT
Flags
,
UINT
Flags
,
REFIID
riid
,
REFIID
riid
,
void
**
ppFactory
void
**
ppFactory
);
);
static
CreateDXGIFactory2_t
impl
=
nullptr
;
static
CreateDXGIFactory2_t
impl
=
nullptr
;
if
(
!
impl
)
{
if
(
!
impl
)
{
auto
dxgidll
=
LoadLibrary
(
TEXT
(
"Dxgi.dll"
));
auto
dxgidll
=
LoadLibrary
(
TEXT
(
"Dxgi.dll"
));
if
(
!
dxgidll
)
if
(
!
dxgidll
)
return
E_NOTIMPL
;
return
E_NOTIMPL
;
...
@@ -164,9 +163,9 @@ HRESULT CreateDXGIFactory2(
...
@@ -164,9 +163,9 @@ HRESULT CreateDXGIFactory2(
return
E_NOTIMPL
;
return
E_NOTIMPL
;
}
}
return
impl
(
Flags
,
riid
,
ppFactory
);
return
impl
(
Flags
,
riid
,
ppFactory
);
}
}
HRESULT
D3DCompile
(
HRESULT
D3DCompile
(
LPCVOID
pSrcData
,
LPCVOID
pSrcData
,
SIZE_T
SrcDataSize
,
SIZE_T
SrcDataSize
,
LPCSTR
pSourceName
,
LPCSTR
pSourceName
,
...
@@ -177,8 +176,8 @@ HRESULT D3DCompile(
...
@@ -177,8 +176,8 @@ HRESULT D3DCompile(
UINT
Flags1
,
UINT
Flags1
,
UINT
Flags2
,
UINT
Flags2
,
ID3DBlob
**
ppCode
,
ID3DBlob
**
ppCode
,
ID3DBlob
**
ppErrorMsgs
ID3DBlob
**
ppErrorMsgs
)
)
{
{
typedef
HRESULT
(
*
D3DCompile_t
)(
typedef
HRESULT
(
*
D3DCompile_t
)(
LPCVOID
pSrcData
,
LPCVOID
pSrcData
,
SIZE_T
SrcDataSize
,
SIZE_T
SrcDataSize
,
...
@@ -190,10 +189,10 @@ HRESULT D3DCompile(
...
@@ -190,10 +189,10 @@ HRESULT D3DCompile(
UINT
Flags1
,
UINT
Flags1
,
UINT
Flags2
,
UINT
Flags2
,
ID3DBlob
**
ppCode
,
ID3DBlob
**
ppCode
,
ID3DBlob
**
ppErrorMsgs
ID3DBlob
**
ppErrorMsgs
);
);
static
D3DCompile_t
impl
=
nullptr
;
static
D3DCompile_t
impl
=
nullptr
;
if
(
!
impl
)
{
if
(
!
impl
)
{
auto
d3dcompilerdll
=
LoadLibrary
(
TEXT
(
"d3dcompiler_47.dll"
));
auto
d3dcompilerdll
=
LoadLibrary
(
TEXT
(
"d3dcompiler_47.dll"
));
if
(
!
d3dcompilerdll
)
if
(
!
d3dcompilerdll
)
return
E_NOTIMPL
;
return
E_NOTIMPL
;
...
@@ -202,48 +201,15 @@ HRESULT D3DCompile(
...
@@ -202,48 +201,15 @@ HRESULT D3DCompile(
return
E_NOTIMPL
;
return
E_NOTIMPL
;
}
}
return
impl
(
pSrcData
,
SrcDataSize
,
pSourceName
,
pDefines
,
pInclude
,
pEntrypoint
,
pTarget
,
Flags1
,
Flags2
,
ppCode
,
ppErrorMsgs
);
return
impl
(
pSrcData
,
SrcDataSize
,
pSourceName
,
pDefines
,
pInclude
,
pEntrypoint
,
pTarget
,
Flags1
,
Flags2
,
ppCode
,
ppErrorMsgs
);
}
JNIEXPORT
jlong
JNICALL
Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_makeDirectXContext
(
JNIEnv
*
env
,
jobject
redrawer
,
jlong
devicePtr
)
{
DirectXDevice
*
d3dDevice
=
fromJavaPointer
<
DirectXDevice
*>
(
devicePtr
);
GrD3DBackendContext
backendContext
=
d3dDevice
->
backendContext
;
return
toJavaPointer
(
GrDirectContext
::
MakeDirect3D
(
backendContext
).
release
());
}
}
JNIEXPORT
jlong
JNICALL
Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_makeDirectXRenderTarget
(
bool
isBlacklisted
(
IDXGIAdapter1
*
hardwareAdapter
)
JNIEnv
*
env
,
jobject
redrawer
,
jlong
devicePtr
,
jint
width
,
jint
height
)
{
DirectXDevice
*
d3dDevice
=
fromJavaPointer
<
DirectXDevice
*>
(
devicePtr
);
if
(
!
d3dDevice
->
isSizeEqualTo
(
width
,
height
))
{
GR_D3D_CALL_ERRCHECK
(
d3dDevice
->
swapChain
->
ResizeBuffers
(
BuffersCount
,
width
,
height
,
DXGI_FORMAT_R8G8B8A8_UNORM
,
0
));
d3dDevice
->
bufferWidth
=
width
;
d3dDevice
->
bufferHeight
=
height
;
}
d3dDevice
->
bufferIndex
=
d3dDevice
->
swapChain
->
GetCurrentBackBufferIndex
();
ID3D12Resource
*
buffer
;
GR_D3D_CALL_ERRCHECK
(
d3dDevice
->
swapChain
->
GetBuffer
(
d3dDevice
->
bufferIndex
,
IID_PPV_ARGS
(
&
buffer
)));
GrD3DTextureResourceInfo
info
(
buffer
,
nullptr
,
D3D12_RESOURCE_STATE_PRESENT
,
DXGI_FORMAT_R8G8B8A8_UNORM
,
1
,
1
,
0
);
GrBackendRenderTarget
*
renderTarget
=
new
GrBackendRenderTarget
(
width
,
height
,
info
);
return
toJavaPointer
(
renderTarget
);
}
bool
isBlacklisted
(
IDXGIAdapter1
*
hardwareAdapter
)
{
{
DXGI_ADAPTER_DESC1
desc
;
DXGI_ADAPTER_DESC1
desc
;
hardwareAdapter
->
GetDesc1
(
&
desc
);
hardwareAdapter
->
GetDesc1
(
&
desc
);
std
::
wstring
currentAdapterName
(
desc
.
Description
);
std
::
wstring
currentAdapterName
(
desc
.
Description
);
for
(
std
::
wstring
name
:
adapterBlacklist
)
{
for
(
std
::
wstring
name
:
adapterBlacklist
)
{
if
(
currentAdapterName
==
name
)
if
(
currentAdapterName
==
name
)
{
{
fwprintf
(
stderr
,
L"Graphics card: %s is blacklisted.
\n
"
,
name
.
c_str
());
fwprintf
(
stderr
,
L"Graphics card: %s is blacklisted.
\n
"
,
name
.
c_str
());
...
@@ -312,13 +278,21 @@ HRESULT D3DCompile(
...
@@ -312,13 +278,21 @@ HRESULT D3DCompile(
d3dDevice
->
backendContext
.
fAdapter
=
hardwareAdapter
;
d3dDevice
->
backendContext
.
fAdapter
=
hardwareAdapter
;
d3dDevice
->
backendContext
.
fDevice
=
device
;
d3dDevice
->
backendContext
.
fDevice
=
device
;
d3dDevice
->
backendContext
.
fQueue
=
queue
;
d3dDevice
->
backendContext
.
fQueue
=
queue
;
d3dDevice
->
backendContext
.
fProtectedContext
=
GrProtected
::
kNo
;
d3dDevice
->
device
=
device
;
d3dDevice
->
device
=
device
;
d3dDevice
->
queue
=
queue
;
d3dDevice
->
queue
=
queue
;
d3dDevice
->
window
=
(
HWND
)
windowHandle
;
// Make the swapchain
return
toJavaPointer
(
d3dDevice
);
HWND
fWindow
=
(
HWND
)
windowHandle
;
}
JNIEXPORT
void
JNICALL
Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_initSwapChain
(
JNIEnv
*
env
,
jobject
redrawer
,
jlong
devicePtr
)
{
DirectXDevice
*
d3dDevice
=
fromJavaPointer
<
DirectXDevice
*>
(
devicePtr
);
// Make the swapchain
gr_cp
<
IDXGIFactory4
>
swapChainFactory
;
gr_cp
<
IDXGIFactory4
>
swapChainFactory
;
GR_D3D_CALL_ERRCHECK
(
CreateDXGIFactory2
(
0
,
IID_PPV_ARGS
(
&
swapChainFactory
)));
GR_D3D_CALL_ERRCHECK
(
CreateDXGIFactory2
(
0
,
IID_PPV_ARGS
(
&
swapChainFactory
)));
...
@@ -326,71 +300,145 @@ HRESULT D3DCompile(
...
@@ -326,71 +300,145 @@ HRESULT D3DCompile(
swapChainDesc
.
BufferCount
=
BuffersCount
;
swapChainDesc
.
BufferCount
=
BuffersCount
;
swapChainDesc
.
Format
=
DXGI_FORMAT_R8G8B8A8_UNORM
;
swapChainDesc
.
Format
=
DXGI_FORMAT_R8G8B8A8_UNORM
;
swapChainDesc
.
BufferUsage
=
DXGI_USAGE_RENDER_TARGET_OUTPUT
;
swapChainDesc
.
BufferUsage
=
DXGI_USAGE_RENDER_TARGET_OUTPUT
;
swapChainDesc
.
SwapEffect
=
DXGI_SWAP_EFFECT_FLIP_
SEQUENTIAL
;
swapChainDesc
.
SwapEffect
=
DXGI_SWAP_EFFECT_FLIP_
DISCARD
;
swapChainDesc
.
SampleDesc
.
Count
=
1
;
swapChainDesc
.
SampleDesc
.
Count
=
1
;
swapChainDesc
.
SampleDesc
.
Quality
=
0
;
swapChainDesc
.
Scaling
=
DXGI_SCALING_NONE
;
swapChainDesc
.
Scaling
=
DXGI_SCALING_NONE
;
DXGI_SWAP_CHAIN_FULLSCREEN_DESC
swapChainFSDesc
=
{};
swapChainFSDesc
.
Windowed
=
TRUE
;
gr_cp
<
IDXGISwapChain1
>
swapChain
;
gr_cp
<
IDXGISwapChain1
>
swapChain
;
GR_D3D_CALL_ERRCHECK
(
swapChainFactory
->
CreateSwapChainForHwnd
(
d3dDevice
->
queue
.
get
(),
fWindow
,
&
swapChainDesc
,
&
swapChainFSDesc
,
nullptr
,
&
swapChain
));
GR_D3D_CALL_ERRCHECK
(
swapChainFactory
->
CreateSwapChainForHwnd
(
d3dDevice
->
queue
.
get
(),
d3dDevice
->
window
,
&
swapChainDesc
,
nullptr
,
nullptr
,
&
swapChain
));
GR_D3D_CALL_ERRCHECK
(
swapChainFactory
->
MakeWindowAssociation
(
d3dDevice
->
window
,
DXGI_MWA_NO_ALT_ENTER
));
GR_D3D_CALL_ERRCHECK
(
swapChain
->
QueryInterface
(
IID_PPV_ARGS
(
&
d3dDevice
->
swapChain
)));
GR_D3D_CALL_ERRCHECK
(
swapChain
->
QueryInterface
(
IID_PPV_ARGS
(
&
d3dDevice
->
swapChain
)));
GR_D3D_CALL_ERRCHECK
(
d3dDevice
->
device
->
CreateFence
(
0
,
D3D12_FENCE_FLAG_NONE
,
IID_PPV_ARGS
(
&
d3dDevice
->
fence
)));
d3dDevice
->
fenceEvent
=
CreateEvent
(
nullptr
,
FALSE
,
FALSE
,
nullptr
);
RECT
windowRect
;
GetWindowRect
(
d3dDevice
->
window
,
&
windowRect
);
unsigned
int
w
=
windowRect
.
right
-
windowRect
.
left
;
unsigned
int
h
=
windowRect
.
bottom
-
windowRect
.
top
;
GR_D3D_CALL_ERRCHECK
(
d3dDevice
->
swapChain
->
ResizeBuffers
(
BuffersCount
,
w
,
h
,
DXGI_FORMAT_R8G8B8A8_UNORM
,
0
));
swapChainFactory
.
reset
(
nullptr
);
swapChainFactory
.
reset
(
nullptr
);
swapChainFactory
.
reset
(
nullptr
);
}
return
toJavaPointer
(
d3dDevice
);
JNIEXPORT
void
JNICALL
Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_initFence
(
JNIEnv
*
env
,
jobject
redrawer
,
jlong
devicePtr
)
{
DirectXDevice
*
d3dDevice
=
fromJavaPointer
<
DirectXDevice
*>
(
devicePtr
);
for
(
int
i
=
0
;
i
<
BuffersCount
;
i
++
)
{
d3dDevice
->
fenceValues
[
i
]
=
10000
;
}
GR_D3D_CALL_ERRCHECK
(
d3dDevice
->
device
->
CreateFence
(
d3dDevice
->
fenceValues
[
0
],
D3D12_FENCE_FLAG_NONE
,
IID_PPV_ARGS
(
&
d3dDevice
->
fence
)));
d3dDevice
->
fenceEvent
=
CreateEvent
(
nullptr
,
FALSE
,
FALSE
,
nullptr
);
SkASSERT
(
d3dDevice
->
fenceEvent
);
}
JNIEXPORT
jlong
JNICALL
Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_makeDirectXContext
(
JNIEnv
*
env
,
jobject
redrawer
,
jlong
devicePtr
)
{
DirectXDevice
*
d3dDevice
=
fromJavaPointer
<
DirectXDevice
*>
(
devicePtr
);
GrD3DBackendContext
backendContext
=
d3dDevice
->
backendContext
;
return
toJavaPointer
(
GrDirectContext
::
MakeDirect3D
(
backendContext
).
release
());
}
JNIEXPORT
jlong
JNICALL
Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_makeDirectXSurface
(
JNIEnv
*
env
,
jobject
redrawer
,
jlong
devicePtr
,
jlong
contextPtr
,
jint
width
,
jint
height
,
jint
index
)
{
DirectXDevice
*
d3dDevice
=
fromJavaPointer
<
DirectXDevice
*>
(
devicePtr
);
GrDirectContext
*
context
=
fromJavaPointer
<
GrDirectContext
*>
(
contextPtr
);
GrD3DTextureResourceInfo
info
(
nullptr
,
nullptr
,
D3D12_RESOURCE_STATE_PRESENT
,
DXGI_FORMAT_R8G8B8A8_UNORM
,
1
,
1
,
0
);
GR_D3D_CALL_ERRCHECK
(
d3dDevice
->
swapChain
->
GetBuffer
(
index
,
IID_PPV_ARGS
(
&
d3dDevice
->
buffers
[
index
])));
info
.
fResource
=
d3dDevice
->
buffers
[
index
];
SkSurfaceProps
surfaceProps
(
0
,
kRGB_H_SkPixelGeometry
);
GrBackendTexture
backendTexture
((
int
)
d3dDevice
->
buffers
[
index
]
->
GetDesc
().
Width
,
(
int
)
d3dDevice
->
buffers
[
index
]
->
GetDesc
().
Height
,
info
);
return
toJavaPointer
(
SkSurface
::
MakeFromBackendTexture
(
context
,
backendTexture
,
kTopLeft_GrSurfaceOrigin
,
0
,
kRGBA_8888_SkColorType
,
SkColorSpace
::
MakeSRGB
(),
&
surfaceProps
)
.
release
());
}
}
JNIEXPORT
void
JNICALL
Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_resizeBuffers
(
JNIEXPORT
void
JNICALL
Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_resizeBuffers
(
JNIEnv
*
env
,
jobject
redrawer
,
jlong
devicePtr
,
jint
width
,
jint
height
)
JNIEnv
*
env
,
jobject
redrawer
,
jlong
devicePtr
,
jint
width
,
jint
height
)
{
{
DirectXDevice
*
d3dDevice
=
fromJavaPointer
<
DirectXDevice
*>
(
devicePtr
);
DirectXDevice
*
d3dDevice
=
fromJavaPointer
<
DirectXDevice
*>
(
devicePtr
);
if
(
!
d3dDevice
->
isSizeEqualTo
(
width
,
height
)
)
for
(
int
i
=
0
;
i
<
BuffersCount
;
i
++
)
{
{
GR_D3D_CALL_ERRCHECK
(
d3dDevice
->
swapChain
->
ResizeBuffers
(
BuffersCount
,
width
,
height
,
DXGI_FORMAT_R8G8B8A8_UNORM
,
0
));
if
(
d3dDevice
->
fence
->
GetCompletedValue
()
<
d3dDevice
->
fenceValues
[
i
])
d3dDevice
->
bufferWidth
=
width
;
{
d3dDevice
->
bufferHeight
=
height
;
GR_D3D_CALL_ERRCHECK
(
d3dDevice
->
fence
->
SetEventOnCompletion
(
d3dDevice
->
fenceValues
[
i
],
d3dDevice
->
fenceEvent
));
WaitForSingleObjectEx
(
d3dDevice
->
fenceEvent
,
INFINITE
,
FALSE
);
}
d3dDevice
->
buffers
[
i
].
reset
(
nullptr
);
}
}
GR_D3D_CALL_ERRCHECK
(
d3dDevice
->
swapChain
->
ResizeBuffers
(
BuffersCount
,
width
,
height
,
DXGI_FORMAT_R8G8B8A8_UNORM
,
0
));
}
}
JNIEXPORT
void
JNICALL
Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_finishFrame
(
JNIEXPORT
void
JNICALL
Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_finishFrame
(
JNIEnv
*
env
,
jobject
redrawer
,
jlong
devicePtr
,
jlong
contextPtr
,
jlong
surfacePtr
,
jboolean
isVsyncEnabled
)
JNIEnv
*
env
,
jobject
redrawer
,
jlong
devicePtr
,
jlong
contextPtr
,
jlong
surfacePtr
,
jboolean
isVsyncEnabled
)
{
{
DirectXDevice
*
d3dDevice
=
fromJavaPointer
<
DirectXDevice
*>
(
devicePtr
);
DirectXDevice
*
d3dDevice
=
fromJavaPointer
<
DirectXDevice
*>
(
devicePtr
);
SkSurface
*
surface
=
fromJavaPointer
<
SkSurface
*>
(
surfacePtr
);
SkSurface
*
surface
=
fromJavaPointer
<
SkSurface
*>
(
surfacePtr
);
GrDirectContext
*
fContext
=
fromJavaPointer
<
GrDirectContext
*>
(
contextPtr
);
GrDirectContext
*
context
=
fromJavaPointer
<
GrDirectContext
*>
(
contextPtr
);
surface
->
flushAndSubmit
();
surface
->
flushAndSubmit
(
true
);
surface
->
flush
(
SkSurface
::
BackendSurfaceAccess
::
kPresent
,
GrFlushInfo
());
surface
->
flush
(
SkSurface
::
BackendSurfaceAccess
::
kPresent
,
GrFlushInfo
());
fC
ontext
->
flush
({});
c
ontext
->
flush
({});
fC
ontext
->
submit
(
true
);
c
ontext
->
submit
(
true
);
// 1 value in [Present(1, 0)] enables vblank wait so this is how vertical sync works in DirectX.
// 1 value in [Present(1, 0)] enables vblank wait so this is how vertical sync works in DirectX.
const
UINT64
fenceValue
=
d3dDevice
->
fenceValues
[
d3dDevice
->
bufferIndex
];
GR_D3D_CALL_ERRCHECK
(
d3dDevice
->
swapChain
->
Present
((
int
)
isVsyncEnabled
,
0
));
GR_D3D_CALL_ERRCHECK
(
d3dDevice
->
swapChain
->
Present
((
int
)
isVsyncEnabled
,
0
));
GR_D3D_CALL_ERRCHECK
(
d3dDevice
->
queue
->
Signal
(
d3dDevice
->
fence
.
get
(),
fenceValue
));
}
const
UINT64
fence
=
d3dDevice
->
fenceValue
;
JNIEXPORT
void
JNICALL
Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_disposeDevice
(
GR_D3D_CALL_ERRCHECK
(
d3dDevice
->
queue
->
Signal
(
d3dDevice
->
fence
.
get
(),
fence
));
JNIEnv
*
env
,
jobject
redrawer
,
jlong
devicePtr
,
jlong
contextPtr
)
d3dDevice
->
fenceValue
++
;
if
(
d3dDevice
->
fence
->
GetCompletedValue
()
<
fence
)
{
{
GR_D3D_CALL_ERRCHECK
(
d3dDevice
->
fence
->
SetEventOnCompletion
(
fence
,
d3dDevice
->
fenceEvent
));
DirectXDevice
*
d3dDevice
=
fromJavaPointer
<
DirectXDevice
*>
(
devicePtr
);
GrDirectContext
*
context
=
fromJavaPointer
<
GrDirectContext
*>
(
contextPtr
);
context
->
flush
({});
context
->
submit
(
true
);
for
(
int
i
=
0
;
i
<
BuffersCount
;
i
++
)
{
if
(
d3dDevice
->
fence
->
GetCompletedValue
()
<
d3dDevice
->
fenceValues
[
i
])
{
GR_D3D_CALL_ERRCHECK
(
d3dDevice
->
fence
->
SetEventOnCompletion
(
d3dDevice
->
fenceValues
[
i
],
d3dDevice
->
fenceEvent
));
WaitForSingleObjectEx
(
d3dDevice
->
fenceEvent
,
INFINITE
,
FALSE
);
WaitForSingleObjectEx
(
d3dDevice
->
fenceEvent
,
INFINITE
,
FALSE
);
}
}
}
}
JNIEXPORT
void
JNICALL
Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_disposeDevice
(
delete
d3dDevice
;
}
JNIEXPORT
jint
JNICALL
Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_getBufferIndex
(
JNIEnv
*
env
,
jobject
redrawer
,
jlong
devicePtr
)
JNIEnv
*
env
,
jobject
redrawer
,
jlong
devicePtr
)
{
{
DirectXDevice
*
d3dDevice
=
fromJavaPointer
<
DirectXDevice
*>
(
devicePtr
);
DirectXDevice
*
d3dDevice
=
fromJavaPointer
<
DirectXDevice
*>
(
devicePtr
);
delete
d3dDevice
;
const
UINT64
fenceValue
=
d3dDevice
->
fenceValues
[
d3dDevice
->
bufferIndex
];
d3dDevice
->
bufferIndex
=
d3dDevice
->
swapChain
->
GetCurrentBackBufferIndex
();
if
(
d3dDevice
->
fence
->
GetCompletedValue
()
<
fenceValue
)
{
GR_D3D_CALL_ERRCHECK
(
d3dDevice
->
fence
->
SetEventOnCompletion
(
fenceValue
,
d3dDevice
->
fenceEvent
));
WaitForSingleObjectEx
(
d3dDevice
->
fenceEvent
,
INFINITE
,
FALSE
);
}
d3dDevice
->
fenceValues
[
d3dDevice
->
bufferIndex
]
=
fenceValue
+
1
;
return
d3dDevice
->
bufferIndex
;
}
}
JNIEXPORT
jstring
JNICALL
Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_getAdapterName
(
JNIEnv
*
env
,
jobject
redrawer
,
jlong
devicePtr
)
{
JNIEXPORT
jstring
JNICALL
Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_getAdapterName
(
JNIEnv
*
env
,
jobject
redrawer
,
jlong
devicePtr
)
DirectXDevice
*
d3dDevice
=
fromJavaPointer
<
DirectXDevice
*>
(
devicePtr
);
{
DirectXDevice
*
d3dDevice
=
fromJavaPointer
<
DirectXDevice
*>
(
devicePtr
);
DXGI_ADAPTER_DESC1
desc
;
DXGI_ADAPTER_DESC1
desc
;
d3dDevice
->
backendContext
.
fAdapter
->
GetDesc1
(
&
desc
);
d3dDevice
->
backendContext
.
fAdapter
->
GetDesc1
(
&
desc
);
...
@@ -400,8 +448,9 @@ HRESULT D3DCompile(
...
@@ -400,8 +448,9 @@ HRESULT D3DCompile(
return
result
;
return
result
;
}
}
JNIEXPORT
jlong
JNICALL
Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_getAdapterMemorySize
(
JNIEnv
*
env
,
jobject
redrawer
,
jlong
devicePtr
)
{
JNIEXPORT
jlong
JNICALL
Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_getAdapterMemorySize
(
JNIEnv
*
env
,
jobject
redrawer
,
jlong
devicePtr
)
DirectXDevice
*
d3dDevice
=
fromJavaPointer
<
DirectXDevice
*>
(
devicePtr
);
{
DirectXDevice
*
d3dDevice
=
fromJavaPointer
<
DirectXDevice
*>
(
devicePtr
);
DXGI_ADAPTER_DESC1
desc
;
DXGI_ADAPTER_DESC1
desc
;
d3dDevice
->
backendContext
.
fAdapter
->
GetDesc1
(
&
desc
);
d3dDevice
->
backendContext
.
fAdapter
->
GetDesc1
(
&
desc
);
...
...
skiko/src/jvmMain/kotlin/org/jetbrains/skiko/context/ContextHandler.kt
View file @
f1b93ac7
...
@@ -47,7 +47,7 @@ internal abstract class ContextHandler(val layer: SkiaLayer) {
...
@@ -47,7 +47,7 @@ internal abstract class ContextHandler(val layer: SkiaLayer) {
destroyContext
()
destroyContext
()
}
}
fun
disposeCanvas
()
{
open
fun
disposeCanvas
()
{
surface
?.
close
()
surface
?.
close
()
renderTarget
?.
close
()
renderTarget
?.
close
()
}
}
...
...
skiko/src/jvmMain/kotlin/org/jetbrains/skiko/context/Direct3DContextHandler.kt
View file @
f1b93ac7
...
@@ -10,6 +10,9 @@ import org.jetbrains.skiko.redrawer.Direct3DRedrawer
...
@@ -10,6 +10,9 @@ import org.jetbrains.skiko.redrawer.Direct3DRedrawer
import
java.lang.ref.Reference
import
java.lang.ref.Reference
internal
class
Direct3DContextHandler
(
layer
:
SkiaLayer
)
:
ContextHandler
(
layer
)
{
internal
class
Direct3DContextHandler
(
layer
:
SkiaLayer
)
:
ContextHandler
(
layer
)
{
private
val
bufferCount
=
2
private
var
surfaces
:
Array
<
Surface
?>
=
arrayOfNulls
(
bufferCount
)
val
directXRedrawer
:
Direct3DRedrawer
val
directXRedrawer
:
Direct3DRedrawer
get
()
=
layer
.
redrawer
!!
as
Direct3DRedrawer
get
()
=
layer
.
redrawer
!!
as
Direct3DRedrawer
...
@@ -22,7 +25,6 @@ internal class Direct3DContextHandler(layer: SkiaLayer) : ContextHandler(layer)
...
@@ -22,7 +25,6 @@ internal class Direct3DContextHandler(layer: SkiaLayer) : ContextHandler(layer)
throw
Exception
(
"Failed to create DirectX12 device."
)
throw
Exception
(
"Failed to create DirectX12 device."
)
}
}
context
=
directXRedrawer
.
makeContext
(
device
)
context
=
directXRedrawer
.
makeContext
(
device
)
val
printDeviceEnabled
=
System
.
getProperty
(
"skiko.hardwareInfo.enabled"
)
==
"true"
if
(
System
.
getProperty
(
"skiko.hardwareInfo.enabled"
)
==
"true"
)
{
if
(
System
.
getProperty
(
"skiko.hardwareInfo.enabled"
)
==
"true"
)
{
println
(
hardwareInfo
())
println
(
hardwareInfo
())
}
}
...
@@ -34,28 +36,51 @@ internal class Direct3DContextHandler(layer: SkiaLayer) : ContextHandler(layer)
...
@@ -34,28 +36,51 @@ internal class Direct3DContextHandler(layer: SkiaLayer) : ContextHandler(layer)
return
true
return
true
}
}
override
fun
initCanvas
()
{
private
var
currentWidth
=
0
disposeCanvas
()
private
var
currentHeight
=
0
private
fun
isSizeChanged
(
width
:
Int
,
height
:
Int
):
Boolean
{
if
(
width
!=
currentWidth
||
height
!=
currentHeight
)
{
currentWidth
=
width
currentHeight
=
height
return
true
}
return
false
}
private
var
isD3DInited
=
false
override
fun
initCanvas
()
{
val
scale
=
layer
.
contentScale
val
scale
=
layer
.
contentScale
val
w
=
(
layer
.
width
*
scale
).
toInt
().
coerceAtLeast
(
0
)
val
w
=
(
layer
.
width
*
scale
).
toInt
().
coerceAtLeast
(
0
)
val
h
=
(
layer
.
height
*
scale
).
toInt
().
coerceAtLeast
(
0
)
val
h
=
(
layer
.
height
*
scale
).
toInt
().
coerceAtLeast
(
0
)
renderTarget
=
directXRedrawer
.
makeRenderTarget
(
device
,
w
,
h
)
if
(
isSizeChanged
(
w
,
h
))
{
disposeCanvas
()
context
?.
flush
()
surface
=
Surface
.
makeFromBackendRenderTarget
(
if
(!
isD3DInited
)
{
context
!!
,
directXRedrawer
.
initSwapChain
(
device
)
renderTarget
!!
,
}
else
{
SurfaceOrigin
.
TOP_LEFT
,
directXRedrawer
.
resizeBuffers
(
device
,
w
,
h
)
SurfaceColorFormat
.
RGBA_8888
,
}
ColorSpace
.
getSRGB
()
)
try
{
for
(
bufferIndex
in
0
..
bufferCount
-
1
)
{
surfaces
[
bufferIndex
]
=
directXRedrawer
.
makeSurface
(
device
,
Native
.
getPtr
(
context
!!
),
w
,
h
,
bufferIndex
)
}
}
finally
{
Reference
.
reachabilityFence
(
context
!!
)
}
if
(!
isD3DInited
)
{
isD3DInited
=
true
directXRedrawer
.
initFence
(
device
)
}
}
surface
=
surfaces
[
directXRedrawer
.
getBufferIndex
(
device
)]
canvas
=
surface
!!
.
canvas
canvas
=
surface
!!
.
canvas
}
}
override
fun
flush
()
{
override
fun
flush
()
{
super
.
flush
()
try
{
try
{
directXRedrawer
.
finishFrame
(
directXRedrawer
.
finishFrame
(
device
,
device
,
...
@@ -69,8 +94,18 @@ internal class Direct3DContextHandler(layer: SkiaLayer) : ContextHandler(layer)
...
@@ -69,8 +94,18 @@ internal class Direct3DContextHandler(layer: SkiaLayer) : ContextHandler(layer)
}
}
override
fun
destroyContext
()
{
override
fun
destroyContext
()
{
try
{
directXRedrawer
.
disposeDevice
(
device
,
Native
.
getPtr
(
context
!!
))
}
finally
{
Reference
.
reachabilityFence
(
context
!!
)
}
context
?.
close
()
context
?.
close
()
directXRedrawer
.
disposeDevice
(
device
)
}
override
fun
disposeCanvas
()
{
for
(
bufferIndex
in
0
..
bufferCount
-
1
)
{
surfaces
[
bufferIndex
]
?.
close
()
}
}
}
override
fun
hardwareInfo
():
String
{
override
fun
hardwareInfo
():
String
{
...
...
skiko/src/jvmMain/kotlin/org/jetbrains/skiko/redrawer/Direct3DRedrawer.kt
View file @
f1b93ac7
...
@@ -3,7 +3,7 @@ package org.jetbrains.skiko.redrawer
...
@@ -3,7 +3,7 @@ package org.jetbrains.skiko.redrawer
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.swing.Swing
import
kotlinx.coroutines.swing.Swing
import
kotlinx.coroutines.withContext
import
kotlinx.coroutines.withContext
import
org.jetbrains.skija.
BackendRenderTarget
import
org.jetbrains.skija.
Surface
import
org.jetbrains.skija.DirectContext
import
org.jetbrains.skija.DirectContext
import
org.jetbrains.skiko.FrameDispatcher
import
org.jetbrains.skiko.FrameDispatcher
import
org.jetbrains.skiko.SkiaLayer
import
org.jetbrains.skiko.SkiaLayer
...
@@ -60,8 +60,8 @@ internal class Direct3DRedrawer(
...
@@ -60,8 +60,8 @@ internal class Direct3DRedrawer(
makeDirectXContext
(
device
)
makeDirectXContext
(
device
)
)
)
fun
make
RenderTarget
(
device
:
Long
,
width
:
Int
,
height
:
Int
)
=
BackendRenderTarget
(
fun
make
Surface
(
device
:
Long
,
context
:
Long
,
width
:
Int
,
height
:
Int
,
index
:
Int
)
=
Surface
(
makeDirectX
RenderTarget
(
device
,
width
,
height
)
makeDirectX
Surface
(
device
,
context
,
width
,
height
,
index
)
)
)
fun
createDevice
():
Long
=
createDirectXDevice
(
layer
.
windowHandle
)
fun
createDevice
():
Long
=
createDirectXDevice
(
layer
.
windowHandle
)
...
@@ -72,10 +72,13 @@ internal class Direct3DRedrawer(
...
@@ -72,10 +72,13 @@ internal class Direct3DRedrawer(
external
fun
createDirectXDevice
(
windowHandle
:
Long
):
Long
external
fun
createDirectXDevice
(
windowHandle
:
Long
):
Long
external
fun
makeDirectXContext
(
device
:
Long
):
Long
external
fun
makeDirectXContext
(
device
:
Long
):
Long
external
fun
makeDirectX
RenderTarget
(
device
:
Long
,
width
:
Int
,
height
:
Int
):
Long
external
fun
makeDirectX
Surface
(
device
:
Long
,
context
:
Long
,
width
:
Int
,
height
:
Int
,
index
:
Int
):
Long
external
fun
resizeBuffers
(
device
:
Long
,
width
:
Int
,
height
:
Int
)
external
fun
resizeBuffers
(
device
:
Long
,
width
:
Int
,
height
:
Int
)
private
external
fun
finishFrame
(
device
:
Long
,
context
:
Long
,
surface
:
Long
,
isVsyncEnabled
:
Boolean
)
private
external
fun
finishFrame
(
device
:
Long
,
context
:
Long
,
surface
:
Long
,
isVsyncEnabled
:
Boolean
)
external
fun
disposeDevice
(
device
:
Long
)
external
fun
disposeDevice
(
device
:
Long
,
context
:
Long
)
external
fun
getBufferIndex
(
device
:
Long
):
Int
external
fun
initSwapChain
(
device
:
Long
)
external
fun
initFence
(
device
:
Long
)
external
fun
getAdapterName
(
device
:
Long
):
String
external
fun
getAdapterName
(
device
:
Long
):
String
external
fun
getAdapterMemorySize
(
device
:
Long
):
Long
external
fun
getAdapterMemorySize
(
device
:
Long
):
Long
}
}
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