Move the index data manager and draw calls into the renderer implementation.

TRAC #22016 Signed-off-by: Daniel Koch Signed-off-by: Geoff Lang Author: Nicolas Capens git-svn-id: https://angleproject.googlecode.com/svn/branches/dx11proto@1492 736b8ea6-26fd-11df-bfd4-992fa37f6226
parent 41cb9428
...@@ -41,10 +41,6 @@ class Surface; ...@@ -41,10 +41,6 @@ class Surface;
namespace gl namespace gl
{ {
struct TranslatedAttribute;
struct TranslatedIndexData;
class Buffer;
class Shader; class Shader;
class Program; class Program;
class ProgramBinary; class ProgramBinary;
...@@ -56,10 +52,8 @@ class Renderbuffer; ...@@ -56,10 +52,8 @@ class Renderbuffer;
class RenderbufferStorage; class RenderbufferStorage;
class Colorbuffer; class Colorbuffer;
class Depthbuffer; class Depthbuffer;
class StreamingIndexBuffer;
class Stencilbuffer; class Stencilbuffer;
class DepthStencilbuffer; class DepthStencilbuffer;
class IndexDataManager;
class Fence; class Fence;
class Query; class Query;
...@@ -376,8 +370,6 @@ class Context ...@@ -376,8 +370,6 @@ class Context
void drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei instances); void drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei instances);
void sync(bool block); // flush/finish void sync(bool block); // flush/finish
void drawLineLoop(GLsizei count, GLenum type, const GLvoid *indices, int minIndex);
void recordInvalidEnum(); void recordInvalidEnum();
void recordInvalidValue(); void recordInvalidValue();
void recordInvalidOperation(); void recordInvalidOperation();
...@@ -433,7 +425,6 @@ class Context ...@@ -433,7 +425,6 @@ class Context
bool applyRenderTarget(bool ignoreViewport); bool applyRenderTarget(bool ignoreViewport);
void applyState(GLenum drawMode); void applyState(GLenum drawMode);
GLenum applyIndexBuffer(const GLvoid *indices, GLsizei count, GLenum mode, GLenum type, TranslatedIndexData *indexInfo);
void applyShaders(); void applyShaders();
void applyTextures(); void applyTextures();
void applyTextures(SamplerType type); void applyTextures(SamplerType type);
...@@ -481,10 +472,6 @@ class Context ...@@ -481,10 +472,6 @@ class Context
std::string mExtensionString; std::string mExtensionString;
std::string mRendererString; std::string mRendererString;
IndexDataManager *mIndexDataManager;
StreamingIndexBuffer *mLineLoopIB;
BindingPointer<Texture> mIncompleteTextures[TEXTURE_TYPE_COUNT]; BindingPointer<Texture> mIncompleteTextures[TEXTURE_TYPE_COUNT];
...@@ -505,7 +492,6 @@ class Context ...@@ -505,7 +492,6 @@ class Context
unsigned int mAppliedTextureSerialPS[MAX_TEXTURE_IMAGE_UNITS]; unsigned int mAppliedTextureSerialPS[MAX_TEXTURE_IMAGE_UNITS];
unsigned int mAppliedTextureSerialVS[MAX_VERTEX_TEXTURE_IMAGE_UNITS_VTF]; unsigned int mAppliedTextureSerialVS[MAX_VERTEX_TEXTURE_IMAGE_UNITS_VTF];
unsigned int mAppliedProgramBinarySerial; unsigned int mAppliedProgramBinarySerial;
unsigned int mAppliedIBSerial;
rx::RenderTarget::Desc mRenderTargetDesc; // D3D9_REPLACE rx::RenderTarget::Desc mRenderTargetDesc; // D3D9_REPLACE
bool mDxUniformsDirty; bool mDxUniformsDirty;
BindingPointer<ProgramBinary> mCurrentProgramBinary; BindingPointer<ProgramBinary> mCurrentProgramBinary;
......
...@@ -107,7 +107,7 @@ void computeRange(GLenum type, const GLvoid *indices, GLsizei count, GLuint *min ...@@ -107,7 +107,7 @@ void computeRange(GLenum type, const GLvoid *indices, GLsizei count, GLuint *min
else UNREACHABLE(); else UNREACHABLE();
} }
GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, Buffer *buffer, const GLvoid *indices, TranslatedIndexData *translated) GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, Buffer *buffer, const GLvoid *indices, TranslatedIndexData *translated, IDirect3DIndexBuffer9 **d3dIndexBuffer, unsigned int *serial)
{ {
if (!mStreamingBufferShort) if (!mStreamingBufferShort)
{ {
...@@ -198,8 +198,8 @@ GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, Buffer *bu ...@@ -198,8 +198,8 @@ GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, Buffer *bu
} }
} }
translated->indexBuffer = indexBuffer->getBuffer(); *d3dIndexBuffer = indexBuffer->getBuffer();
translated->serial = indexBuffer->getSerial(); *serial = indexBuffer->getSerial();
translated->startIndex = streamOffset / indexSize(format); translated->startIndex = streamOffset / indexSize(format);
if (buffer) if (buffer)
......
...@@ -31,9 +31,6 @@ struct TranslatedIndexData ...@@ -31,9 +31,6 @@ struct TranslatedIndexData
UINT minIndex; UINT minIndex;
UINT maxIndex; UINT maxIndex;
UINT startIndex; UINT startIndex;
IDirect3DIndexBuffer9 *indexBuffer;
unsigned int serial;
}; };
class IndexBuffer class IndexBuffer
...@@ -128,7 +125,7 @@ class IndexDataManager ...@@ -128,7 +125,7 @@ class IndexDataManager
IndexDataManager(rx::Renderer9 *renderer); IndexDataManager(rx::Renderer9 *renderer);
virtual ~IndexDataManager(); virtual ~IndexDataManager();
GLenum prepareIndexData(GLenum type, GLsizei count, Buffer *arrayElementBuffer, const GLvoid *indices, TranslatedIndexData *translated); GLenum prepareIndexData(GLenum type, GLsizei count, Buffer *arrayElementBuffer, const GLvoid *indices, TranslatedIndexData *translated, IDirect3DIndexBuffer9 **indexBuffer, unsigned int *serial);
StaticIndexBuffer *getCountingIndices(GLsizei count); StaticIndexBuffer *getCountingIndices(GLsizei count);
private: private:
......
...@@ -41,6 +41,8 @@ namespace gl ...@@ -41,6 +41,8 @@ namespace gl
{ {
class ProgramBinary; class ProgramBinary;
class VertexAttribute; class VertexAttribute;
class Buffer;
struct TranslatedIndexData;
} }
namespace rx namespace rx
...@@ -69,9 +71,6 @@ class Renderer ...@@ -69,9 +71,6 @@ class Renderer
virtual int generateConfigs(ConfigDesc **configDescList) = 0; virtual int generateConfigs(ConfigDesc **configDescList) = 0;
virtual void deleteConfigs(ConfigDesc *configDescList) = 0; virtual void deleteConfigs(ConfigDesc *configDescList) = 0;
virtual void startScene() = 0;
virtual void endScene() = 0;
virtual void sync(bool block) = 0; virtual void sync(bool block) = 0;
virtual SwapChain *createSwapChain(HWND window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat) = 0; virtual SwapChain *createSwapChain(HWND window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat) = 0;
...@@ -93,8 +92,12 @@ class Renderer ...@@ -93,8 +92,12 @@ class Renderer
virtual bool applyRenderTarget(gl::Framebuffer *frameBuffer) = 0; virtual bool applyRenderTarget(gl::Framebuffer *frameBuffer) = 0;
virtual void applyShaders(gl::ProgramBinary *programBinary) = 0; virtual void applyShaders(gl::ProgramBinary *programBinary) = 0;
virtual bool applyPrimitiveType(GLenum primitiveType, GLsizei elementCount) = 0;
virtual GLenum applyVertexBuffer(gl::ProgramBinary *programBinary, gl::VertexAttribute vertexAttributes[], GLint first, GLsizei count, GLsizei instances) = 0;
virtual GLenum applyIndexBuffer(const GLvoid *indices, gl::Buffer *elementArrayBuffer, GLsizei count, GLenum mode, GLenum type, gl::TranslatedIndexData *indexInfo) = 0;
virtual GLenum applyVertexBuffer(gl::ProgramBinary *programBinary, gl::VertexAttribute vertexAttributes[], GLint first, GLsizei count, GLsizei instances, GLsizei *repeatDraw) = 0; virtual void drawArrays(GLenum mode, GLsizei count, GLsizei instances) = 0;
virtual void drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, gl::Buffer *elementArrayBuffer) = 0;
virtual void clear(const gl::ClearParameters &clearParams, gl::Framebuffer *frameBuffer) = 0; virtual void clear(const gl::ClearParameters &clearParams, gl::Framebuffer *frameBuffer) = 0;
......
...@@ -230,16 +230,6 @@ void Renderer11::deleteConfigs(ConfigDesc *configDescList) ...@@ -230,16 +230,6 @@ void Renderer11::deleteConfigs(ConfigDesc *configDescList)
delete [] (configDescList); delete [] (configDescList);
} }
void Renderer11::startScene()
{
// TODO: nop in d3d11?
}
void Renderer11::endScene()
{
// TODO: nop in d3d11?
}
void Renderer11::sync(bool block) void Renderer11::sync(bool block)
{ {
// TODO // TODO
...@@ -436,7 +426,15 @@ bool Renderer11::setViewport(const gl::Rectangle& viewport, float zNear, float z ...@@ -436,7 +426,15 @@ bool Renderer11::setViewport(const gl::Rectangle& viewport, float zNear, float z
return true; return true;
} }
bool Renderer11::applyRenderTarget(gl::Framebuffer *frameBuffer) bool Renderer11::applyPrimitiveType(GLenum mode, GLsizei count)
{
// TODO
UNIMPLEMENTED();
return false;
}
bool Renderer11::applyRenderTarget(gl::Framebuffer *framebuffer)
{ {
// TODO // TODO
UNIMPLEMENTED(); UNIMPLEMENTED();
...@@ -447,7 +445,15 @@ bool Renderer11::applyRenderTarget(gl::Framebuffer *frameBuffer) ...@@ -447,7 +445,15 @@ bool Renderer11::applyRenderTarget(gl::Framebuffer *frameBuffer)
return true; return true;
} }
GLenum Renderer11::applyVertexBuffer(gl::ProgramBinary *programBinary, gl::VertexAttribute vertexAttributes[], GLint first, GLsizei count, GLsizei instances, GLsizei *repeatDraw) GLenum Renderer11::applyVertexBuffer(gl::ProgramBinary *programBinary, gl::VertexAttribute vertexAttributes[], GLint first, GLsizei count, GLsizei instances)
{
// TODO
UNIMPLEMENTED();
return GL_OUT_OF_MEMORY;
}
GLenum Renderer11::applyIndexBuffer(const GLvoid *indices, gl::Buffer *elementArrayBuffer, GLsizei count, GLenum mode, GLenum type, gl::TranslatedIndexData *indexInfo)
{ {
// TODO // TODO
UNIMPLEMENTED(); UNIMPLEMENTED();
...@@ -455,6 +461,18 @@ GLenum Renderer11::applyVertexBuffer(gl::ProgramBinary *programBinary, gl::Verte ...@@ -455,6 +461,18 @@ GLenum Renderer11::applyVertexBuffer(gl::ProgramBinary *programBinary, gl::Verte
return GL_OUT_OF_MEMORY; return GL_OUT_OF_MEMORY;
} }
void Renderer11::drawArrays(GLenum mode, GLsizei count, GLsizei instances)
{
// TODO
UNIMPLEMENTED();
}
void Renderer11::drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, gl::Buffer *elementArrayBuffer)
{
// TODO
UNIMPLEMENTED();
}
void Renderer11::applyShaders(gl::ProgramBinary *programBinary) void Renderer11::applyShaders(gl::ProgramBinary *programBinary)
{ {
// TODO // TODO
......
...@@ -41,9 +41,6 @@ class Renderer11 : public Renderer ...@@ -41,9 +41,6 @@ class Renderer11 : public Renderer
virtual int generateConfigs(ConfigDesc **configDescList); virtual int generateConfigs(ConfigDesc **configDescList);
virtual void deleteConfigs(ConfigDesc *configDescList); virtual void deleteConfigs(ConfigDesc *configDescList);
virtual void startScene();
virtual void endScene();
virtual void sync(bool block); virtual void sync(bool block);
virtual SwapChain *createSwapChain(HWND window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat); virtual SwapChain *createSwapChain(HWND window, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat);
...@@ -63,10 +60,14 @@ class Renderer11 : public Renderer ...@@ -63,10 +60,14 @@ class Renderer11 : public Renderer
unsigned int renderTargetWidth, unsigned int renderTargetHeight, unsigned int renderTargetWidth, unsigned int renderTargetHeight,
gl::ProgramBinary *currentProgram, bool forceSetUniforms); gl::ProgramBinary *currentProgram, bool forceSetUniforms);
virtual bool applyPrimitiveType(GLenum mode, GLsizei count);
virtual bool applyRenderTarget(gl::Framebuffer *frameBuffer); virtual bool applyRenderTarget(gl::Framebuffer *frameBuffer);
virtual void applyShaders(gl::ProgramBinary *programBinary); virtual void applyShaders(gl::ProgramBinary *programBinary);
virtual GLenum applyVertexBuffer(gl::ProgramBinary *programBinary, gl::VertexAttribute vertexAttributes[], GLint first, GLsizei count, GLsizei instances);
virtual GLenum applyIndexBuffer(const GLvoid *indices, gl::Buffer *elementArrayBuffer, GLsizei count, GLenum mode, GLenum type, gl::TranslatedIndexData *indexInfo);
virtual GLenum applyVertexBuffer(gl::ProgramBinary *programBinary, gl::VertexAttribute vertexAttributes[], GLint first, GLsizei count, GLsizei instances, GLsizei *repeatDraw); virtual void drawArrays(GLenum mode, GLsizei count, GLsizei instances);
virtual void drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, gl::Buffer *elementArrayBuffer);
virtual void clear(const gl::ClearParameters &clearParams, gl::Framebuffer *frameBuffer); virtual void clear(const gl::ClearParameters &clearParams, gl::Framebuffer *frameBuffer);
......
...@@ -23,13 +23,17 @@ ...@@ -23,13 +23,17 @@
#include <D3Dcompiler.h> #include <D3Dcompiler.h>
#include "common/angleutils.h" #include "common/angleutils.h"
#include "libGLESv2/Context.h"
#include "libGLESv2/renderer/ShaderCache.h" #include "libGLESv2/renderer/ShaderCache.h"
#include "libGLESv2/renderer/VertexDeclarationCache.h" #include "libGLESv2/renderer/VertexDeclarationCache.h"
#include "libGLESv2/renderer/Renderer.h" #include "libGLESv2/renderer/Renderer.h"
#include "libGLESv2/IndexDataManager.h"
namespace gl namespace gl
{ {
class VertexDataManager; class VertexDataManager;
class StreamingIndexBuffer;
struct TranslatedAttribute;
} }
namespace rx namespace rx
...@@ -49,8 +53,8 @@ class Renderer9 : public Renderer ...@@ -49,8 +53,8 @@ class Renderer9 : public Renderer
virtual int generateConfigs(ConfigDesc **configDescList); virtual int generateConfigs(ConfigDesc **configDescList);
virtual void deleteConfigs(ConfigDesc *configDescList); virtual void deleteConfigs(ConfigDesc *configDescList);
virtual void startScene(); void startScene();
virtual void endScene(); void endScene();
virtual void sync(bool block); virtual void sync(bool block);
...@@ -93,8 +97,12 @@ class Renderer9 : public Renderer ...@@ -93,8 +97,12 @@ class Renderer9 : public Renderer
virtual bool applyRenderTarget(gl::Framebuffer *frameBuffer); virtual bool applyRenderTarget(gl::Framebuffer *frameBuffer);
virtual void applyShaders(gl::ProgramBinary *programBinary); virtual void applyShaders(gl::ProgramBinary *programBinary);
virtual bool applyPrimitiveType(GLenum primitiveType, GLsizei elementCount);
virtual GLenum applyVertexBuffer(gl::ProgramBinary *programBinary, gl::VertexAttribute vertexAttributes[], GLint first, GLsizei count, GLsizei instances);
virtual GLenum applyIndexBuffer(const GLvoid *indices, gl::Buffer *elementArrayBuffer, GLsizei count, GLenum mode, GLenum type, gl::TranslatedIndexData *indexInfo);
virtual GLenum applyVertexBuffer(gl::ProgramBinary *programBinary, gl::VertexAttribute vertexAttributes[], GLint first, GLsizei count, GLsizei instances, GLsizei *repeatDraw); virtual void drawArrays(GLenum mode, GLsizei count, GLsizei instances);
virtual void drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, gl::Buffer *elementArrayBuffer);
virtual void clear(const gl::ClearParameters &clearParams, gl::Framebuffer *frameBuffer); virtual void clear(const gl::ClearParameters &clearParams, gl::Framebuffer *frameBuffer);
...@@ -166,6 +174,8 @@ class Renderer9 : public Renderer ...@@ -166,6 +174,8 @@ class Renderer9 : public Renderer
private: private:
DISALLOW_COPY_AND_ASSIGN(Renderer9); DISALLOW_COPY_AND_ASSIGN(Renderer9);
void drawLineLoop(GLsizei count, GLenum type, const GLvoid *indices, int minIndex, gl::Buffer *elementArrayBuffer);
void getMultiSampleSupport(D3DFORMAT format, bool *multiSampleArray); void getMultiSampleSupport(D3DFORMAT format, bool *multiSampleArray);
bool copyToRenderTarget(IDirect3DSurface9 *dest, IDirect3DSurface9 *source, bool fromManaged); bool copyToRenderTarget(IDirect3DSurface9 *dest, IDirect3DSurface9 *source, bool fromManaged);
...@@ -209,6 +219,11 @@ class Renderer9 : public Renderer ...@@ -209,6 +219,11 @@ class Renderer9 : public Renderer
D3DCAPS9 mDeviceCaps; D3DCAPS9 mDeviceCaps;
D3DADAPTER_IDENTIFIER9 mAdapterIdentifier; D3DADAPTER_IDENTIFIER9 mAdapterIdentifier;
D3DPRIMITIVETYPE mPrimitiveType;
int mPrimitiveCount;
GLsizei mRepeatDraw;
gl::TranslatedIndexData mIndexInfo;
bool mSceneStarted; bool mSceneStarted;
bool mSupportsNonPower2Textures; bool mSupportsNonPower2Textures;
bool mSupportsTextureFilterAnisotropy; bool mSupportsTextureFilterAnisotropy;
...@@ -255,6 +270,8 @@ class Renderer9 : public Renderer ...@@ -255,6 +270,8 @@ class Renderer9 : public Renderer
gl::Color mCurBlendColor; gl::Color mCurBlendColor;
GLuint mCurSampleMask; GLuint mCurSampleMask;
unsigned int mAppliedIBSerial;
// A pool of event queries that are currently unused. // A pool of event queries that are currently unused.
std::vector<IDirect3DQuery9*> mEventQueryPool; std::vector<IDirect3DQuery9*> mEventQueryPool;
VertexShaderCache mVertexShaderCache; VertexShaderCache mVertexShaderCache;
...@@ -262,6 +279,9 @@ class Renderer9 : public Renderer ...@@ -262,6 +279,9 @@ class Renderer9 : public Renderer
gl::VertexDataManager *mVertexDataManager; gl::VertexDataManager *mVertexDataManager;
VertexDeclarationCache mVertexDeclarationCache; VertexDeclarationCache mVertexDeclarationCache;
gl::IndexDataManager *mIndexDataManager;
gl::StreamingIndexBuffer *mLineLoopIB;
}; };
} }
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#define LIBGLESV2_RENDERER_VERTEXDECLARATIONCACHE_H_ #define LIBGLESV2_RENDERER_VERTEXDECLARATIONCACHE_H_
#include "libGLESv2/Context.h" #include "libGLESv2/Context.h"
#include "libGLESv2/VertexDataManager.h"
namespace gl namespace gl
{ {
......
...@@ -236,46 +236,6 @@ void ConvertMinFilter(GLenum minFilter, D3DTEXTUREFILTERTYPE *d3dMinFilter, D3DT ...@@ -236,46 +236,6 @@ void ConvertMinFilter(GLenum minFilter, D3DTEXTUREFILTERTYPE *d3dMinFilter, D3DT
} }
} }
bool ConvertPrimitiveType(GLenum primitiveType, GLsizei elementCount,
D3DPRIMITIVETYPE *d3dPrimitiveType, int *d3dPrimitiveCount)
{
switch (primitiveType)
{
case GL_POINTS:
*d3dPrimitiveType = D3DPT_POINTLIST;
*d3dPrimitiveCount = elementCount;
break;
case GL_LINES:
*d3dPrimitiveType = D3DPT_LINELIST;
*d3dPrimitiveCount = elementCount / 2;
break;
case GL_LINE_LOOP:
*d3dPrimitiveType = D3DPT_LINESTRIP;
*d3dPrimitiveCount = elementCount - 1; // D3D doesn't support line loops, so we draw the last line separately
break;
case GL_LINE_STRIP:
*d3dPrimitiveType = D3DPT_LINESTRIP;
*d3dPrimitiveCount = elementCount - 1;
break;
case GL_TRIANGLES:
*d3dPrimitiveType = D3DPT_TRIANGLELIST;
*d3dPrimitiveCount = elementCount / 3;
break;
case GL_TRIANGLE_STRIP:
*d3dPrimitiveType = D3DPT_TRIANGLESTRIP;
*d3dPrimitiveCount = elementCount - 2;
break;
case GL_TRIANGLE_FAN:
*d3dPrimitiveType = D3DPT_TRIANGLEFAN;
*d3dPrimitiveCount = elementCount - 2;
break;
default:
return false;
}
return true;
}
D3DFORMAT ConvertRenderbufferFormat(GLenum format) D3DFORMAT ConvertRenderbufferFormat(GLenum format)
{ {
switch (format) switch (format)
......
...@@ -35,8 +35,6 @@ D3DCUBEMAP_FACES ConvertCubeFace(GLenum cubeFace); ...@@ -35,8 +35,6 @@ D3DCUBEMAP_FACES ConvertCubeFace(GLenum cubeFace);
DWORD ConvertColorMask(bool red, bool green, bool blue, bool alpha); DWORD ConvertColorMask(bool red, bool green, bool blue, bool alpha);
D3DTEXTUREFILTERTYPE ConvertMagFilter(GLenum magFilter, float maxAnisotropy); D3DTEXTUREFILTERTYPE ConvertMagFilter(GLenum magFilter, float maxAnisotropy);
void ConvertMinFilter(GLenum minFilter, D3DTEXTUREFILTERTYPE *d3dMinFilter, D3DTEXTUREFILTERTYPE *d3dMipFilter, float maxAnisotropy); void ConvertMinFilter(GLenum minFilter, D3DTEXTUREFILTERTYPE *d3dMinFilter, D3DTEXTUREFILTERTYPE *d3dMipFilter, float maxAnisotropy);
bool ConvertPrimitiveType(GLenum primitiveType, GLsizei elementCount,
D3DPRIMITIVETYPE *d3dPrimitiveType, int *d3dPrimitiveCount);
D3DFORMAT ConvertRenderbufferFormat(GLenum format); D3DFORMAT ConvertRenderbufferFormat(GLenum format);
D3DMULTISAMPLE_TYPE GetMultisampleTypeFromSamples(GLsizei samples); D3DMULTISAMPLE_TYPE GetMultisampleTypeFromSamples(GLsizei samples);
......
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