Commit 1aa728e3 by Jamie Madill Committed by Commit Bot

D3D11: Faster multisample depth resolve.

We can use a faster path to write out depth values. Previous attempts were foiled by a D3D quirk: we need to clear a new DSV before we write to it the first time. Multisampled stencil resolve still requires a CPU readback. BUG=angleproject:1710 Change-Id: I7a7d2f929644f61854d51dd096a8aa599ad648bd Reviewed-on: https://chromium-review.googlesource.com/435571Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 6483eb62
...@@ -712,7 +712,7 @@ gl::Error Blit11::initResources() ...@@ -712,7 +712,7 @@ gl::Error Blit11::initResources()
d3d11::SetDebugName(mScissorDisabledRasterizerState, "Blit11 no scissoring rasterizer state"); d3d11::SetDebugName(mScissorDisabledRasterizerState, "Blit11 no scissoring rasterizer state");
D3D11_DEPTH_STENCIL_DESC depthStencilDesc; D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
depthStencilDesc.DepthEnable = true; depthStencilDesc.DepthEnable = TRUE;
depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
depthStencilDesc.DepthFunc = D3D11_COMPARISON_ALWAYS; depthStencilDesc.DepthFunc = D3D11_COMPARISON_ALWAYS;
depthStencilDesc.StencilEnable = FALSE; depthStencilDesc.StencilEnable = FALSE;
...@@ -1969,17 +1969,13 @@ gl::Error Blit11::getSwizzleShader(GLenum type, ...@@ -1969,17 +1969,13 @@ gl::Error Blit11::getSwizzleShader(GLenum type,
gl::ErrorOrResult<TextureHelper11> Blit11::resolveDepth(RenderTarget11 *depth) gl::ErrorOrResult<TextureHelper11> Blit11::resolveDepth(RenderTarget11 *depth)
{ {
// Multisampled depth stencil SRVs are not available in feature level 10.0 // Multisampled depth stencil SRVs are not available in feature level 10.0
if (mRenderer->getRenderer11DeviceCaps().featureLevel <= D3D_FEATURE_LEVEL_10_0) ASSERT(mRenderer->getRenderer11DeviceCaps().featureLevel > D3D_FEATURE_LEVEL_10_0);
{
return gl::InternalError() << "Resolving multisampled depth stencil textures is not "
"supported in feature level 10.0.";
}
const auto &extents = depth->getExtents(); const auto &extents = depth->getExtents();
ID3D11Device *device = mRenderer->getDevice(); ID3D11Device *device = mRenderer->getDevice();
ID3D11DeviceContext *context = mRenderer->getDeviceContext(); ID3D11DeviceContext *context = mRenderer->getDeviceContext();
ANGLE_TRY(initResolveDepthStencil(extents)); ANGLE_TRY(initResolveDepthOnly(depth->getFormatSet(), extents));
// Notify the Renderer that all state should be invalidated. // Notify the Renderer that all state should be invalidated.
mRenderer->markAllStateDirty(); mRenderer->markAllStateDirty();
...@@ -1990,8 +1986,8 @@ gl::ErrorOrResult<TextureHelper11> Blit11::resolveDepth(RenderTarget11 *depth) ...@@ -1990,8 +1986,8 @@ gl::ErrorOrResult<TextureHelper11> Blit11::resolveDepth(RenderTarget11 *depth)
context->VSSetShader(mResolveDepthStencilVS.resolve(device), nullptr, 0); context->VSSetShader(mResolveDepthStencilVS.resolve(device), nullptr, 0);
context->GSSetShader(nullptr, nullptr, 0); context->GSSetShader(nullptr, nullptr, 0);
context->RSSetState(nullptr); context->RSSetState(nullptr);
context->OMSetDepthStencilState(nullptr, 0xFFFFFFFF); context->OMSetDepthStencilState(mDepthStencilState.Get(), 0xFFFFFFFF);
context->OMSetRenderTargets(1, mResolvedDepthStencilRTView.GetAddressOf(), nullptr); context->OMSetRenderTargets(0, nullptr, mResolvedDepthDSView.Get());
context->OMSetBlendState(nullptr, nullptr, 0xFFFFFFF); context->OMSetBlendState(nullptr, nullptr, 0xFFFFFFF);
// Set the viewport // Set the viewport
...@@ -2013,38 +2009,63 @@ gl::ErrorOrResult<TextureHelper11> Blit11::resolveDepth(RenderTarget11 *depth) ...@@ -2013,38 +2009,63 @@ gl::ErrorOrResult<TextureHelper11> Blit11::resolveDepth(RenderTarget11 *depth)
// Trigger the blit on the GPU. // Trigger the blit on the GPU.
context->Draw(6, 0); context->Draw(6, 0);
gl::Box copyBox(0, 0, 0, extents.width, extents.height, 1); return TextureHelper11::MakeAndReference(mResolvedDepth.getResource(),
mResolvedDepth.getFormatSet());
}
const auto &copyFunction = GetCopyDepthStencilFunction(depth->getInternalFormat()); gl::Error Blit11::initResolveDepthOnly(const d3d11::Format &format, const gl::Extents &extents)
const auto &dsFormatSet = depth->getFormatSet(); {
const auto &dsDxgiInfo = d3d11::GetDXGIFormatSizeInfo(dsFormatSet.texFormat); if (mResolvedDepth.valid() && extents == mResolvedDepth.getExtents() &&
format.texFormat == mResolvedDepth.getFormat())
{
return gl::NoError();
}
ID3D11Device *device = mRenderer->getDevice();
D3D11_TEXTURE2D_DESC textureDesc;
textureDesc.Width = extents.width;
textureDesc.Height = extents.height;
textureDesc.MipLevels = 1;
textureDesc.ArraySize = 1;
textureDesc.Format = format.texFormat;
textureDesc.SampleDesc.Count = 1;
textureDesc.SampleDesc.Quality = 0;
textureDesc.Usage = D3D11_USAGE_DEFAULT;
textureDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
textureDesc.CPUAccessFlags = 0;
textureDesc.MiscFlags = 0;
ID3D11Texture2D *destTex = nullptr; ID3D11Texture2D *resolvedDepth = nullptr;
HRESULT hr = device->CreateTexture2D(&textureDesc, nullptr, &resolvedDepth);
D3D11_TEXTURE2D_DESC destDesc;
destDesc.Width = extents.width;
destDesc.Height = extents.height;
destDesc.MipLevels = 1;
destDesc.ArraySize = 1;
destDesc.Format = dsFormatSet.texFormat;
destDesc.SampleDesc.Count = 1;
destDesc.SampleDesc.Quality = 0;
destDesc.Usage = D3D11_USAGE_DEFAULT;
destDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
destDesc.CPUAccessFlags = 0;
destDesc.MiscFlags = 0;
HRESULT hr = device->CreateTexture2D(&destDesc, nullptr, &destTex);
if (FAILED(hr)) if (FAILED(hr))
{ {
return gl::OutOfMemory() << "Error creating depth resolve dest texture, " << hr; return gl::OutOfMemory() << "Failed to allocate resolved depth texture, " << hr;
} }
d3d11::SetDebugName(destTex, "resolveDepthDest"); d3d11::SetDebugName(resolvedDepth, "Blit11::mResolvedDepth");
TextureHelper11 dest = TextureHelper11::MakeAndPossess2D(destTex, depth->getFormatSet()); mResolvedDepth = TextureHelper11::MakeAndPossess2D(resolvedDepth, format);
ANGLE_TRY(copyAndConvert(mResolvedDepthStencil, 0, copyBox, extents, dest, 0, copyBox, extents,
nullptr, 0, 0, 0, 8, dsDxgiInfo.pixelBytes, copyFunction)); D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
return dest; dsvDesc.Flags = 0;
dsvDesc.Format = format.dsvFormat;
dsvDesc.Texture2D.MipSlice = 0;
dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
hr = device->CreateDepthStencilView(mResolvedDepth.getResource(), &dsvDesc,
mResolvedDepthDSView.ReleaseAndGetAddressOf());
if (FAILED(hr))
{
return gl::OutOfMemory() << "Failed to allocate Blit11::mResolvedDepthDSView, " << hr;
}
d3d11::SetDebugName(mResolvedDepthDSView, "Blit11::mResolvedDepthDSView");
// Possibly D3D11 bug or undefined behaviour: Clear the DSV so that our first render
// works as expected. Otherwise the results of the first use seem to be incorrect.
auto context = mRenderer->getDeviceContext();
context->ClearDepthStencilView(mResolvedDepthDSView.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0);
return gl::NoError();
} }
gl::Error Blit11::initResolveDepthStencil(const gl::Extents &extents) gl::Error Blit11::initResolveDepthStencil(const gl::Extents &extents)
...@@ -2052,6 +2073,7 @@ gl::Error Blit11::initResolveDepthStencil(const gl::Extents &extents) ...@@ -2052,6 +2073,7 @@ gl::Error Blit11::initResolveDepthStencil(const gl::Extents &extents)
// Check if we need to recreate depth stencil view // Check if we need to recreate depth stencil view
if (mResolvedDepthStencil.valid() && extents == mResolvedDepthStencil.getExtents()) if (mResolvedDepthStencil.valid() && extents == mResolvedDepthStencil.getExtents())
{ {
ASSERT(mResolvedDepthStencil.getFormat() == DXGI_FORMAT_R32G32_FLOAT);
return gl::NoError(); return gl::NoError();
} }
...@@ -2104,11 +2126,7 @@ gl::ErrorOrResult<TextureHelper11> Blit11::resolveStencil(RenderTarget11 *depthS ...@@ -2104,11 +2126,7 @@ gl::ErrorOrResult<TextureHelper11> Blit11::resolveStencil(RenderTarget11 *depthS
bool alsoDepth) bool alsoDepth)
{ {
// Multisampled depth stencil SRVs are not available in feature level 10.0 // Multisampled depth stencil SRVs are not available in feature level 10.0
if (mRenderer->getRenderer11DeviceCaps().featureLevel <= D3D_FEATURE_LEVEL_10_0) ASSERT(mRenderer->getRenderer11DeviceCaps().featureLevel > D3D_FEATURE_LEVEL_10_0);
{
return gl::InternalError() << "Resolving multisampled depth stencil textures is not "
"supported in feature level 10.0.";
}
const auto &extents = depthStencil->getExtents(); const auto &extents = depthStencil->getExtents();
......
...@@ -253,6 +253,7 @@ class Blit11 : angle::NonCopyable ...@@ -253,6 +253,7 @@ class Blit11 : angle::NonCopyable
void clearShaderMap(); void clearShaderMap();
void releaseResolveDepthStencilResources(); void releaseResolveDepthStencilResources();
gl::Error initResolveDepthOnly(const d3d11::Format &format, const gl::Extents &extents);
gl::Error initResolveDepthStencil(const gl::Extents &extents); gl::Error initResolveDepthStencil(const gl::Extents &extents);
Renderer11 *mRenderer; Renderer11 *mRenderer;
...@@ -287,6 +288,8 @@ class Blit11 : angle::NonCopyable ...@@ -287,6 +288,8 @@ class Blit11 : angle::NonCopyable
angle::ComPtr<ID3D11ShaderResourceView> mStencilSRV; angle::ComPtr<ID3D11ShaderResourceView> mStencilSRV;
TextureHelper11 mResolvedDepthStencil; TextureHelper11 mResolvedDepthStencil;
angle::ComPtr<ID3D11RenderTargetView> mResolvedDepthStencilRTView; angle::ComPtr<ID3D11RenderTargetView> mResolvedDepthStencilRTView;
TextureHelper11 mResolvedDepth;
angle::ComPtr<ID3D11DepthStencilView> mResolvedDepthDSView;
}; };
} // namespace rx } // namespace rx
......
...@@ -22,7 +22,7 @@ Texture2DMS<uint2> Stencil : register(t1); ...@@ -22,7 +22,7 @@ Texture2DMS<uint2> Stencil : register(t1);
void PS_ResolveDepth(in float4 position : SV_Position, void PS_ResolveDepth(in float4 position : SV_Position,
in float2 texCoord : TEXCOORD0, in float2 texCoord : TEXCOORD0,
out float depth : SV_Target0) out float depth : SV_Depth)
{ {
// MS samplers must use Load // MS samplers must use Load
uint width, height, samples; uint width, height, samples;
......
...@@ -23,13 +23,13 @@ ...@@ -23,13 +23,13 @@
// //
// Name Index Mask Register SysValue Format Used // Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------ // -------------------- ----- ------ -------- -------- ------- ------
// SV_Target 0 x 0 TARGET float x // SV_Depth 0 N/A oDepth DEPTH float YES
// //
ps_4_1 ps_4_1
dcl_globalFlags refactoringAllowed dcl_globalFlags refactoringAllowed
dcl_resource_texture2dms(0) (float,float,float,float) t0 dcl_resource_texture2dms(0) (float,float,float,float) t0
dcl_input_ps linear v1.xy dcl_input_ps linear v1.xy
dcl_output o0.x dcl_output oDepth
dcl_temps 1 dcl_temps 1
resinfo_uint r0.xy, l(0), t0.xyzw resinfo_uint r0.xy, l(0), t0.xyzw
utof r0.xy, r0.xyxx utof r0.xy, r0.xyxx
...@@ -37,46 +37,46 @@ mul r0.xy, r0.xyxx, v1.xyxx ...@@ -37,46 +37,46 @@ mul r0.xy, r0.xyxx, v1.xyxx
ftou r0.xy, r0.xyxx ftou r0.xy, r0.xyxx
mov r0.zw, l(0,0,0,0) mov r0.zw, l(0,0,0,0)
ldms r0.x, r0.xyzw, t0.xyzw, l(0) ldms r0.x, r0.xyzw, t0.xyzw, l(0)
mov o0.x, r0.x mov oDepth, r0.x
ret ret
// Approximately 8 instruction slots used // Approximately 8 instruction slots used
#endif #endif
const BYTE g_PS_ResolveDepth[] = { const BYTE g_PS_ResolveDepth[] = {
68, 88, 66, 67, 205, 219, 191, 201, 103, 134, 243, 76, 11, 91, 23, 182, 42, 8, 17, 68, 88, 66, 67, 106, 43, 8, 187, 21, 3, 136, 11, 227, 249, 190, 191, 97, 127, 18,
173, 1, 0, 0, 0, 184, 2, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 176, 0, 212, 1, 0, 0, 0, 176, 2, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 176, 0,
0, 0, 8, 1, 0, 0, 60, 1, 0, 0, 60, 2, 0, 0, 82, 68, 69, 70, 116, 0, 0, 8, 1, 0, 0, 60, 1, 0, 0, 52, 2, 0, 0, 82, 68, 69, 70, 116,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 28, 0, 0, 0,
1, 4, 255, 255, 0, 1, 0, 0, 66, 0, 0, 0, 60, 0, 0, 0, 2, 0, 0, 1, 4, 255, 255, 0, 1, 0, 0, 66, 0, 0, 0, 60, 0, 0, 0, 2, 0, 0,
0, 5, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
0, 0, 1, 0, 0, 0, 68, 101, 112, 116, 104, 0, 77, 105, 99, 114, 111, 115, 111, 0, 0, 1, 0, 0, 0, 68, 101, 112, 116, 104, 0, 77, 105, 99, 114, 111, 115, 111,
102, 116, 32, 40, 82, 41, 32, 72, 76, 83, 76, 32, 83, 104, 97, 100, 101, 114, 32, 102, 116, 32, 40, 82, 41, 32, 72, 76, 83, 76, 32, 83, 104, 97, 100, 101, 114, 32,
67, 111, 109, 112, 105, 108, 101, 114, 32, 54, 46, 51, 46, 57, 54, 48, 48, 46, 49, 67, 111, 109, 112, 105, 108, 101, 114, 32, 54, 46, 51, 46, 57, 54, 48, 48, 46, 49,
54, 51, 56, 52, 0, 73, 83, 71, 78, 80, 0, 0, 0, 2, 0, 0, 0, 8, 0, 54, 51, 56, 52, 0, 73, 83, 71, 78, 80, 0, 0, 0, 2, 0, 0, 0, 8, 0,
0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0,
0, 0, 0, 15, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 1, 0, 0, 0, 3, 3, 0, 0, 83, 86, 95, 80, 111, 115, 105, 3, 0, 0, 0, 1, 0, 0, 0, 3, 3, 0, 0, 83, 86, 95, 80, 111, 115, 105,
116, 105, 111, 110, 0, 84, 69, 88, 67, 79, 79, 82, 68, 0, 171, 171, 171, 79, 83, 116, 105, 111, 110, 0, 84, 69, 88, 67, 79, 79, 82, 68, 0, 171, 171, 171, 79, 83,
71, 78, 44, 0, 0, 0, 1, 0, 0, 0, 8, 0, 0, 0, 32, 0, 0, 0, 0, 71, 78, 44, 0, 0, 0, 1, 0, 0, 0, 8, 0, 0, 0, 32, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 255, 255, 255, 255, 1, 14, 0, 0,
83, 86, 95, 84, 97, 114, 103, 101, 116, 0, 171, 171, 83, 72, 68, 82, 248, 0, 0, 83, 86, 95, 68, 101, 112, 116, 104, 0, 171, 171, 171, 83, 72, 68, 82, 240, 0, 0,
0, 65, 0, 0, 0, 62, 0, 0, 0, 106, 8, 0, 1, 88, 32, 0, 4, 0, 112, 0, 65, 0, 0, 0, 60, 0, 0, 0, 106, 8, 0, 1, 88, 32, 0, 4, 0, 112,
16, 0, 0, 0, 0, 0, 85, 85, 0, 0, 98, 16, 0, 3, 50, 16, 16, 0, 1, 16, 0, 0, 0, 0, 0, 85, 85, 0, 0, 98, 16, 0, 3, 50, 16, 16, 0, 1,
0, 0, 0, 101, 0, 0, 3, 18, 32, 16, 0, 0, 0, 0, 0, 104, 0, 0, 2, 0, 0, 0, 101, 0, 0, 2, 1, 192, 0, 0, 104, 0, 0, 2, 1, 0, 0, 0,
1, 0, 0, 0, 61, 16, 0, 7, 50, 0, 16, 0, 0, 0, 0, 0, 1, 64, 0, 61, 16, 0, 7, 50, 0, 16, 0, 0, 0, 0, 0, 1, 64, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 70, 126, 16, 0, 0, 0, 0, 0, 86, 0, 0, 5, 50, 0, 0, 70, 126, 16, 0, 0, 0, 0, 0, 86, 0, 0, 5, 50, 0, 16, 0, 0, 0,
16, 0, 0, 0, 0, 0, 70, 0, 16, 0, 0, 0, 0, 0, 56, 0, 0, 7, 50, 0, 0, 70, 0, 16, 0, 0, 0, 0, 0, 56, 0, 0, 7, 50, 0, 16, 0, 0,
0, 16, 0, 0, 0, 0, 0, 70, 0, 16, 0, 0, 0, 0, 0, 70, 16, 16, 0, 0, 0, 0, 70, 0, 16, 0, 0, 0, 0, 0, 70, 16, 16, 0, 1, 0, 0, 0,
1, 0, 0, 0, 28, 0, 0, 5, 50, 0, 16, 0, 0, 0, 0, 0, 70, 0, 16, 28, 0, 0, 5, 50, 0, 16, 0, 0, 0, 0, 0, 70, 0, 16, 0, 0, 0, 0,
0, 0, 0, 0, 0, 54, 0, 0, 8, 194, 0, 16, 0, 0, 0, 0, 0, 2, 64, 0, 54, 0, 0, 8, 194, 0, 16, 0, 0, 0, 0, 0, 2, 64, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 0, 9, 18,
0, 0, 9, 18, 0, 16, 0, 0, 0, 0, 0, 70, 14, 16, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 70, 14, 16, 0, 0, 0, 0, 0, 70, 126, 16, 0,
70, 126, 16, 0, 0, 0, 0, 0, 1, 64, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 1, 64, 0, 0, 0, 0, 0, 0, 54, 0, 0, 4, 1, 192, 0,
5, 18, 32, 16, 0, 0, 0, 0, 0, 10, 0, 16, 0, 0, 0, 0, 0, 62, 0, 0, 10, 0, 16, 0, 0, 0, 0, 0, 62, 0, 0, 1, 83, 84, 65, 84, 116, 0,
0, 1, 83, 84, 65, 84, 116, 0, 0, 0, 8, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1,
0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 0, 0, 0, 0};
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