Sharing for buffer, texture and renderbuffer objects.

TRAC #12496 Derive Renderbuffer, Texture and Buffer from RefCountObject. This class keeps a reference count for all objects that need cross-context reference counting, and also the object id. Restructure Renderbuffers to create a wrapper object and a storage object. Use BindingPointer for all binding points instead of binding by object id. Signed-off-by: Shannon Woods Signed-off-by: Daniel Koch git-svn-id: https://angleproject.googlecode.com/svn/trunk@364 736b8ea6-26fd-11df-bfd4-992fa37f6226
parent 0d25b005
......@@ -186,6 +186,8 @@
'libGLESv2/mathutil.h',
'libGLESv2/Program.cpp',
'libGLESv2/Program.h',
'libGLESv2/RefCountObject.cpp',
'libGLESv2/RefCountObject.h',
'libGLESv2/Renderbuffer.cpp',
'libGLESv2/Renderbuffer.h',
'libGLESv2/Shader.cpp',
......
......@@ -13,7 +13,7 @@
namespace gl
{
Buffer::Buffer()
Buffer::Buffer(GLuint id) : RefCountObject(id)
{
mContents = NULL;
mSize = 0;
......
......@@ -18,14 +18,15 @@
#include <GLES2/gl2.h>
#include "common/angleutils.h"
#include "libGLESv2/RefCountObject.h"
namespace gl
{
class Buffer
class Buffer : public RefCountObject
{
public:
Buffer();
explicit Buffer(GLuint id);
virtual ~Buffer();
......
......@@ -120,15 +120,15 @@ Context::Context(const egl::Config *config, const gl::Context *shareContext)
// In order that access to these initial textures not be lost, they are treated as texture
// objects all of whose names are 0.
mTexture2DZero = new Texture2D();
mTextureCubeMapZero = new TextureCubeMap();
mTexture2DZero = new Texture2D(0);
mTextureCubeMapZero = new TextureCubeMap(0);
mColorbufferZero = NULL;
mDepthStencilbufferZero = NULL;
mState.activeSampler = 0;
mState.arrayBuffer = 0;
mState.elementArrayBuffer = 0;
bindArrayBuffer(0);
bindElementArrayBuffer(0);
bindTextureCubeMap(0);
bindTexture2D(0);
bindFramebuffer(0);
......@@ -136,14 +136,6 @@ Context::Context(const egl::Config *config, const gl::Context *shareContext)
for (int type = 0; type < SAMPLER_TYPE_COUNT; type++)
{
for (int sampler = 0; sampler < MAX_TEXTURE_IMAGE_UNITS; sampler++)
{
mState.samplerTexture[type][sampler] = 0;
}
}
for (int type = 0; type < SAMPLER_TYPE_COUNT; type++)
{
mIncompleteTextures[type] = NULL;
}
......@@ -181,27 +173,43 @@ Context::~Context()
mState.currentProgram = 0;
}
while (!mFramebufferMap.empty())
{
deleteFramebuffer(mFramebufferMap.begin()->first);
}
for (int type = 0; type < SAMPLER_TYPE_COUNT; type++)
{
for (int sampler = 0; sampler < MAX_TEXTURE_IMAGE_UNITS; sampler++)
{
mState.samplerTexture[type][sampler].set(NULL);
}
}
for (int type = 0; type < SAMPLER_TYPE_COUNT; type++)
{
delete mIncompleteTextures[type];
}
for (int i = 0; i < MAX_VERTEX_ATTRIBS; i++)
{
mState.vertexAttribute[i].mBoundBuffer.set(NULL);
}
mState.arrayBuffer.set(NULL);
mState.elementArrayBuffer.set(NULL);
mState.texture2D.set(NULL);
mState.textureCubeMap.set(NULL);
mState.renderbuffer.set(NULL);
delete mTexture2DZero;
delete mTextureCubeMapZero;
delete mColorbufferZero;
delete mDepthStencilbufferZero;
delete mBufferBackEnd;
delete mVertexDataManager;
delete mIndexDataManager;
delete mBlit;
while (!mFramebufferMap.empty())
{
deleteFramebuffer(mFramebufferMap.begin()->first);
}
if (mMaskedClearSavedState)
{
mMaskedClearSavedState->Release();
......@@ -242,17 +250,11 @@ void Context::makeCurrent(egl::Display *display, egl::Surface *surface)
IDirect3DSurface9 *defaultRenderTarget = surface->getRenderTarget();
IDirect3DSurface9 *depthStencil = surface->getDepthStencil();
Framebuffer *framebufferZero = new Framebuffer(0);
Colorbuffer *colorbufferZero = new Colorbuffer(defaultRenderTarget);
DepthStencilbuffer *depthStencilbufferZero = new DepthStencilbuffer(depthStencil);
Framebuffer *framebufferZero = new DefaultFramebuffer(colorbufferZero, depthStencilbufferZero);
setFramebufferZero(framebufferZero);
setColorbufferZero(colorbufferZero);
setDepthStencilbufferZero(depthStencilbufferZero);
framebufferZero->setColorbuffer(GL_RENDERBUFFER, 0);
framebufferZero->setDepthbuffer(GL_RENDERBUFFER, 0);
framebufferZero->setStencilbuffer(GL_RENDERBUFFER, 0);
defaultRenderTarget->Release();
......@@ -667,12 +669,12 @@ GLuint Context::getFramebufferHandle() const
GLuint Context::getRenderbufferHandle() const
{
return mState.renderbuffer;
return mState.renderbuffer.id();
}
GLuint Context::getArrayBufferHandle() const
{
return mState.arrayBuffer;
return mState.arrayBuffer.id();
}
void Context::setVertexAttribEnabled(unsigned int attribNum, bool enabled)
......@@ -685,10 +687,10 @@ const AttributeState &Context::getVertexAttribState(unsigned int attribNum)
return mState.vertexAttribute[attribNum];
}
void Context::setVertexAttribState(unsigned int attribNum, GLuint boundBuffer, GLint size, GLenum type, bool normalized,
void Context::setVertexAttribState(unsigned int attribNum, Buffer *boundBuffer, GLint size, GLenum type, bool normalized,
GLsizei stride, const void *pointer)
{
mState.vertexAttribute[attribNum].mBoundBuffer = boundBuffer;
mState.vertexAttribute[attribNum].mBoundBuffer.set(boundBuffer);
mState.vertexAttribute[attribNum].mSize = size;
mState.vertexAttribute[attribNum].mType = type;
mState.vertexAttribute[attribNum].mNormalized = normalized;
......@@ -854,39 +856,39 @@ void Context::bindArrayBuffer(unsigned int buffer)
{
mResourceManager->checkBufferAllocation(buffer);
mState.arrayBuffer = buffer;
mState.arrayBuffer.set(getBuffer(buffer));
}
void Context::bindElementArrayBuffer(unsigned int buffer)
{
mResourceManager->checkBufferAllocation(buffer);
mState.elementArrayBuffer = buffer;
mState.elementArrayBuffer.set(getBuffer(buffer));
}
void Context::bindTexture2D(GLuint texture)
{
mResourceManager->checkTextureAllocation(texture, SAMPLER_2D);
mState.texture2D = texture;
mState.texture2D.set(getTexture(texture));
mState.samplerTexture[SAMPLER_2D][mState.activeSampler] = texture;
mState.samplerTexture[SAMPLER_2D][mState.activeSampler].set(mState.texture2D.get());
}
void Context::bindTextureCubeMap(GLuint texture)
{
mResourceManager->checkTextureAllocation(texture, SAMPLER_CUBE);
mState.textureCubeMap = texture;
mState.textureCubeMap.set(getTexture(texture));
mState.samplerTexture[SAMPLER_CUBE][mState.activeSampler] = texture;
mState.samplerTexture[SAMPLER_CUBE][mState.activeSampler].set(mState.textureCubeMap.get());
}
void Context::bindFramebuffer(GLuint framebuffer)
{
if (!getFramebuffer(framebuffer))
{
mFramebufferMap[framebuffer] = new Framebuffer(framebuffer);
mFramebufferMap[framebuffer] = new Framebuffer();
}
mState.framebuffer = framebuffer;
......@@ -894,7 +896,9 @@ void Context::bindFramebuffer(GLuint framebuffer)
void Context::bindRenderbuffer(GLuint renderbuffer)
{
mState.renderbuffer = renderbuffer;
mResourceManager->checkRenderbufferAllocation(renderbuffer);
mState.renderbuffer.set(getRenderbuffer(renderbuffer));
}
void Context::useProgram(GLuint program)
......@@ -925,22 +929,10 @@ void Context::setFramebufferZero(Framebuffer *buffer)
mFramebufferMap[0] = buffer;
}
void Context::setColorbufferZero(Colorbuffer *buffer)
{
delete mColorbufferZero;
mColorbufferZero = buffer;
}
void Context::setDepthStencilbufferZero(DepthStencilbuffer *buffer)
{
delete mDepthStencilbufferZero;
mDepthStencilbufferZero = buffer;
}
void Context::setRenderbuffer(Renderbuffer *renderbuffer)
void Context::setRenderbufferStorage(RenderbufferStorage *renderbuffer)
{
mResourceManager->deleteRenderbuffer(mState.renderbuffer);
mResourceManager->setRenderbuffer(mState.renderbuffer, renderbuffer);
Renderbuffer *renderbufferObject = mState.renderbuffer.get();
renderbufferObject->setStorage(renderbuffer);
}
Framebuffer *Context::getFramebuffer(unsigned int handle)
......@@ -957,71 +949,14 @@ Framebuffer *Context::getFramebuffer(unsigned int handle)
}
}
Colorbuffer *Context::getColorbuffer(GLuint handle)
{
if (handle != 0)
{
Renderbuffer *renderbuffer = mResourceManager->getRenderbuffer(handle);
if (renderbuffer && renderbuffer->isColorbuffer())
{
return static_cast<Colorbuffer*>(renderbuffer);
}
}
else // Special case: 0 refers to different initial render targets based on the attachment type
{
return mColorbufferZero;
}
return NULL;
}
DepthStencilbuffer *Context::getDepthbuffer(GLuint handle)
{
if (handle != 0)
{
Renderbuffer *renderbuffer = mResourceManager->getRenderbuffer(handle);
if (renderbuffer && renderbuffer->isDepthbuffer())
{
return static_cast<DepthStencilbuffer*>(renderbuffer);
}
}
else // Special case: 0 refers to different initial render targets based on the attachment type
{
return mDepthStencilbufferZero;
}
return NULL;
}
DepthStencilbuffer *Context::getStencilbuffer(GLuint handle)
{
if (handle != 0)
{
Renderbuffer *renderbuffer = mResourceManager->getRenderbuffer(handle);
if (renderbuffer && renderbuffer->isStencilbuffer())
{
return static_cast<DepthStencilbuffer*>(renderbuffer);
}
}
else
{
return mDepthStencilbufferZero;
}
return NULL;
}
Buffer *Context::getArrayBuffer()
{
return mResourceManager->getBuffer(mState.arrayBuffer);
return mState.arrayBuffer.get();
}
Buffer *Context::getElementArrayBuffer()
{
return mResourceManager->getBuffer(mState.elementArrayBuffer);
return mState.elementArrayBuffer.get();
}
Program *Context::getCurrentProgram()
......@@ -1031,27 +966,27 @@ Program *Context::getCurrentProgram()
Texture2D *Context::getTexture2D()
{
if (mState.texture2D == 0) // Special case: 0 refers to different initial textures based on the target
if (mState.texture2D.id() == 0) // Special case: 0 refers to different initial textures based on the target
{
return mTexture2DZero;
}
return (Texture2D*)mResourceManager->getTexture(mState.texture2D);
return static_cast<Texture2D*>(mState.texture2D.get());
}
TextureCubeMap *Context::getTextureCubeMap()
{
if (mState.textureCubeMap == 0) // Special case: 0 refers to different initial textures based on the target
if (mState.textureCubeMap.id() == 0) // Special case: 0 refers to different initial textures based on the target
{
return mTextureCubeMapZero;
}
return (TextureCubeMap*)mResourceManager->getTexture(mState.textureCubeMap);
return static_cast<TextureCubeMap*>(mState.textureCubeMap.get());
}
Texture *Context::getSamplerTexture(unsigned int sampler, SamplerType type)
{
GLuint texid = mState.samplerTexture[type][sampler];
GLuint texid = mState.samplerTexture[type][sampler].id();
if (texid == 0)
{
......@@ -1063,7 +998,7 @@ Texture *Context::getSamplerTexture(unsigned int sampler, SamplerType type)
}
}
return mResourceManager->getTexture(texid);
return mState.samplerTexture[type][sampler].get();
}
bool Context::getBooleanv(GLenum pname, GLboolean *params)
......@@ -1160,10 +1095,10 @@ bool Context::getIntegerv(GLenum pname, GLint *params)
case GL_NUM_COMPRESSED_TEXTURE_FORMATS: *params = 0; break;
case GL_COMPRESSED_TEXTURE_FORMATS: /* no compressed texture formats are supported */ break;
case GL_SHADER_BINARY_FORMATS: /* no shader binary formats are supported */ break;
case GL_ARRAY_BUFFER_BINDING: *params = mState.arrayBuffer; break;
case GL_ELEMENT_ARRAY_BUFFER_BINDING: *params = mState.elementArrayBuffer; break;
case GL_ARRAY_BUFFER_BINDING: *params = mState.arrayBuffer.id(); break;
case GL_ELEMENT_ARRAY_BUFFER_BINDING: *params = mState.elementArrayBuffer.id(); break;
case GL_FRAMEBUFFER_BINDING: *params = mState.framebuffer; break;
case GL_RENDERBUFFER_BINDING: *params = mState.renderbuffer; break;
case GL_RENDERBUFFER_BINDING: *params = mState.renderbuffer.id(); break;
case GL_CURRENT_PROGRAM: *params = mState.currentProgram; break;
case GL_PACK_ALIGNMENT: *params = mState.packAlignment; break;
case GL_UNPACK_ALIGNMENT: *params = mState.unpackAlignment; break;
......@@ -1281,7 +1216,7 @@ bool Context::getIntegerv(GLenum pname, GLint *params)
return false;
}
*params = mState.samplerTexture[SAMPLER_2D][mState.activeSampler];
*params = mState.samplerTexture[SAMPLER_2D][mState.activeSampler].id();
}
break;
case GL_TEXTURE_BINDING_CUBE_MAP:
......@@ -1292,7 +1227,7 @@ bool Context::getIntegerv(GLenum pname, GLint *params)
return false;
}
*params = mState.samplerTexture[SAMPLER_CUBE][mState.activeSampler];
*params = mState.samplerTexture[SAMPLER_CUBE][mState.activeSampler].id();
}
break;
default:
......@@ -1852,7 +1787,7 @@ GLenum Context::applyVertexBuffer(const TranslatedIndexData &indexInfo)
// Applies the indices and element array bindings to the Direct3D 9 device
GLenum Context::applyIndexBuffer(const void *indices, GLsizei count, GLenum mode, GLenum type, TranslatedIndexData *indexInfo)
{
GLenum err = mIndexDataManager->preRenderValidate(mode, type, count, mResourceManager->getBuffer(mState.elementArrayBuffer), indices, indexInfo);
GLenum err = mIndexDataManager->preRenderValidate(mode, type, count, mState.elementArrayBuffer.get(), indices, indexInfo);
if (err == GL_NO_ERROR)
{
......@@ -2608,21 +2543,21 @@ void Context::detachBuffer(GLuint buffer)
// If a buffer object is deleted while it is bound, all bindings to that object in the current context
// (i.e. in the thread that called Delete-Buffers) are reset to zero.
if (mState.arrayBuffer == buffer)
if (mState.arrayBuffer.id() == buffer)
{
mState.arrayBuffer = 0;
mState.arrayBuffer.set(NULL);
}
if (mState.elementArrayBuffer == buffer)
if (mState.elementArrayBuffer.id() == buffer)
{
mState.elementArrayBuffer = 0;
mState.elementArrayBuffer.set(NULL);
}
for (int attribute = 0; attribute < MAX_VERTEX_ATTRIBS; attribute++)
{
if (mState.vertexAttribute[attribute].mBoundBuffer == buffer)
if (mState.vertexAttribute[attribute].mBoundBuffer.id() == buffer)
{
mState.vertexAttribute[attribute].mBoundBuffer = 0;
mState.vertexAttribute[attribute].mBoundBuffer.set(NULL);
}
}
}
......@@ -2637,9 +2572,9 @@ void Context::detachTexture(GLuint texture)
{
for (int sampler = 0; sampler < MAX_TEXTURE_IMAGE_UNITS; sampler++)
{
if (mState.samplerTexture[type][sampler] == texture)
if (mState.samplerTexture[type][sampler].id() == texture)
{
mState.samplerTexture[type][sampler] = 0;
mState.samplerTexture[type][sampler].set(NULL);
}
}
}
......@@ -2675,7 +2610,7 @@ void Context::detachRenderbuffer(GLuint renderbuffer)
// If a renderbuffer that is currently bound to RENDERBUFFER is deleted, it is as though BindRenderbuffer
// had been executed with the target RENDERBUFFER and name of zero.
if (mState.renderbuffer == renderbuffer)
if (mState.renderbuffer.id() == renderbuffer)
{
bindRenderbuffer(0);
}
......@@ -2709,7 +2644,7 @@ Texture *Context::getIncompleteTexture(SamplerType type)
case SAMPLER_2D:
{
Texture2D *incomplete2d = new Texture2D();
Texture2D *incomplete2d = new Texture2D(Texture::INCOMPLETE_TEXTURE_ID);
incomplete2d->setImage(0, GL_RGBA, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 1, color);
t = incomplete2d;
}
......@@ -2717,7 +2652,7 @@ Texture *Context::getIncompleteTexture(SamplerType type)
case SAMPLER_CUBE:
{
TextureCubeMap *incompleteCube = new TextureCubeMap();
TextureCubeMap *incompleteCube = new TextureCubeMap(Texture::INCOMPLETE_TEXTURE_ID);
incompleteCube->setImagePosX(0, GL_RGBA, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 1, color);
incompleteCube->setImageNegX(0, GL_RGBA, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 1, color);
......
......@@ -20,6 +20,7 @@
#include "common/angleutils.h"
#include "libGLESv2/ResourceManager.h"
#include "libGLESv2/RefCountObject.h"
namespace egl
{
......@@ -41,6 +42,7 @@ class Texture2D;
class TextureCubeMap;
class Framebuffer;
class Renderbuffer;
class RenderbufferStorage;
class Colorbuffer;
class Depthbuffer;
class Stencilbuffer;
......@@ -85,7 +87,7 @@ class AttributeState
{
public:
AttributeState()
: mType(GL_FLOAT), mSize(0), mNormalized(false), mStride(0), mPointer(NULL), mBoundBuffer(0), mEnabled(false)
: mType(GL_FLOAT), mSize(0), mNormalized(false), mStride(0), mPointer(NULL), mEnabled(false)
{
mCurrentValue[0] = 0;
mCurrentValue[1] = 0;
......@@ -100,7 +102,7 @@ class AttributeState
GLsizei mStride; // 0 means natural stride
const void *mPointer;
GLuint mBoundBuffer; // Captured when VertexArrayPointer is called.
BindingPointer<Buffer> mBoundBuffer; // Captured when VertexArrayPointer is called.
bool mEnabled; // From Enable/DisableVertexAttribArray
......@@ -175,16 +177,16 @@ struct State
bool depthMask;
int activeSampler; // Active texture unit selector - GL_TEXTURE0
GLuint arrayBuffer;
GLuint elementArrayBuffer;
GLuint texture2D;
GLuint textureCubeMap;
BindingPointer<Buffer> arrayBuffer;
BindingPointer<Buffer> elementArrayBuffer;
BindingPointer<Texture> texture2D;
BindingPointer<Texture> textureCubeMap;
GLuint framebuffer;
GLuint renderbuffer;
BindingPointer<Renderbuffer> renderbuffer;
GLuint currentProgram;
AttributeState vertexAttribute[MAX_VERTEX_ATTRIBS];
GLuint samplerTexture[SAMPLER_TYPE_COUNT][MAX_TEXTURE_IMAGE_UNITS];
BindingPointer<Texture> samplerTexture[SAMPLER_TYPE_COUNT][MAX_TEXTURE_IMAGE_UNITS];
GLint unpackAlignment;
GLint packAlignment;
......@@ -278,7 +280,7 @@ class Context
void setVertexAttribEnabled(unsigned int attribNum, bool enabled);
const AttributeState &getVertexAttribState(unsigned int attribNum);
void setVertexAttribState(unsigned int attribNum, GLuint boundBuffer, GLint size, GLenum type,
void setVertexAttribState(unsigned int attribNum, Buffer *boundBuffer, GLint size, GLenum type,
bool normalized, GLsizei stride, const void *pointer);
const void *getVertexAttribPointer(unsigned int attribNum) const;
......@@ -303,7 +305,7 @@ class Context
void deleteProgram(GLuint program);
void deleteTexture(GLuint texture);
void deleteRenderbuffer(GLuint renderbuffer);
// Framebuffers are owned by the Context, so these methods do not pass through
GLuint createFramebuffer();
void deleteFramebuffer(GLuint framebuffer);
......@@ -317,9 +319,8 @@ class Context
void useProgram(GLuint program);
void setFramebufferZero(Framebuffer *framebuffer);
void setColorbufferZero(Colorbuffer *renderbuffer);
void setDepthStencilbufferZero(DepthStencilbuffer *depthStencilBuffer);
void setRenderbuffer(Renderbuffer *renderbuffer);
void setRenderbufferStorage(RenderbufferStorage *renderbuffer);
void setVertexAttrib(GLuint index, const GLfloat *values);
......@@ -329,9 +330,6 @@ class Context
Texture *getTexture(GLuint handle);
Framebuffer *getFramebuffer(GLuint handle);
Renderbuffer *getRenderbuffer(GLuint handle);
Colorbuffer *getColorbuffer(GLuint handle);
DepthStencilbuffer *getDepthbuffer(GLuint handle);
DepthStencilbuffer *getStencilbuffer(GLuint handle);
Buffer *getArrayBuffer();
Buffer *getElementArrayBuffer();
......
......@@ -16,85 +16,109 @@
namespace gl
{
Framebuffer::Framebuffer(GLuint handle) : mHandle(handle)
Framebuffer::Framebuffer()
{
mColorbufferType = GL_NONE;
mColorbufferHandle = 0;
mDepthbufferType = GL_NONE;
mDepthbufferHandle = 0;
mStencilbufferType = GL_NONE;
mStencilbufferHandle = 0;
}
Framebuffer::~Framebuffer()
{
mColorbufferPointer.set(NULL);
mDepthbufferPointer.set(NULL);
mStencilbufferPointer.set(NULL);
}
Renderbuffer *Framebuffer::lookupRenderbuffer(GLenum type, GLuint handle) const
{
gl::Context *context = gl::getContext();
Renderbuffer *buffer = NULL;
if (type == GL_NONE)
{
buffer = NULL;
}
else if (type == GL_RENDERBUFFER)
{
buffer = context->getRenderbuffer(handle);
}
else if (IsTextureTarget(type))
{
buffer = context->getTexture(handle)->getColorbuffer(type);
}
else
{
UNREACHABLE();
}
return buffer;
}
void Framebuffer::setColorbuffer(GLenum type, GLuint colorbuffer)
{
mColorbufferType = type;
mColorbufferHandle = colorbuffer;
mColorbufferPointer.set(lookupRenderbuffer(type, colorbuffer));
}
void Framebuffer::setDepthbuffer(GLenum type, GLuint depthbuffer)
{
mDepthbufferType = type;
mDepthbufferHandle = depthbuffer;
mDepthbufferPointer.set(lookupRenderbuffer(type, depthbuffer));
}
void Framebuffer::setStencilbuffer(GLenum type, GLuint stencilbuffer)
{
mStencilbufferType = type;
mStencilbufferHandle = stencilbuffer;
mStencilbufferPointer.set(lookupRenderbuffer(type, stencilbuffer));
}
void Framebuffer::detachTexture(GLuint texture)
{
if (mColorbufferHandle == texture && IsTextureTarget(mColorbufferType))
if (mColorbufferPointer.id() == texture && IsTextureTarget(mColorbufferType))
{
mColorbufferType = GL_NONE;
mColorbufferHandle = 0;
mColorbufferPointer.set(NULL);
}
if (mDepthbufferHandle == texture && IsTextureTarget(mDepthbufferType))
if (mDepthbufferPointer.id() == texture && IsTextureTarget(mDepthbufferType))
{
mDepthbufferType = GL_NONE;
mDepthbufferHandle = 0;
mDepthbufferPointer.set(NULL);
}
if (mStencilbufferHandle == texture && IsTextureTarget(mStencilbufferType))
if (mStencilbufferPointer.id() == texture && IsTextureTarget(mStencilbufferType))
{
mStencilbufferType = GL_NONE;
mStencilbufferHandle = 0;
mStencilbufferPointer.set(NULL);
}
}
void Framebuffer::detachRenderbuffer(GLuint renderbuffer)
{
if (mColorbufferHandle == renderbuffer && mColorbufferType == GL_RENDERBUFFER)
if (mColorbufferPointer.id() == renderbuffer && mColorbufferType == GL_RENDERBUFFER)
{
mColorbufferType = GL_NONE;
mColorbufferHandle = 0;
mColorbufferPointer.set(NULL);
}
if (mDepthbufferHandle == renderbuffer && mDepthbufferType == GL_RENDERBUFFER)
if (mDepthbufferPointer.id() == renderbuffer && mDepthbufferType == GL_RENDERBUFFER)
{
mDepthbufferType = GL_NONE;
mDepthbufferHandle = 0;
mDepthbufferPointer.set(NULL);
}
if (mStencilbufferHandle == renderbuffer && mStencilbufferType == GL_RENDERBUFFER)
if (mStencilbufferPointer.id() == renderbuffer && mStencilbufferType == GL_RENDERBUFFER)
{
mStencilbufferType = GL_NONE;
mStencilbufferHandle = 0;
mStencilbufferPointer.set(NULL);
}
}
unsigned int Framebuffer::getRenderTargetSerial()
{
Renderbuffer *colorbuffer = getColorbuffer();
Renderbuffer *colorbuffer = mColorbufferPointer.get();
if (colorbuffer)
{
......@@ -106,7 +130,7 @@ unsigned int Framebuffer::getRenderTargetSerial()
IDirect3DSurface9 *Framebuffer::getRenderTarget()
{
Renderbuffer *colorbuffer = getColorbuffer();
Renderbuffer *colorbuffer = mColorbufferPointer.get();
if (colorbuffer)
{
......@@ -118,8 +142,7 @@ IDirect3DSurface9 *Framebuffer::getRenderTarget()
unsigned int Framebuffer::getDepthbufferSerial()
{
gl::Context *context = gl::getContext();
DepthStencilbuffer *depthbuffer = context->getDepthbuffer(mDepthbufferHandle);
Renderbuffer *depthbuffer = mDepthbufferPointer.get();
if (depthbuffer)
{
......@@ -131,61 +154,44 @@ unsigned int Framebuffer::getDepthbufferSerial()
Colorbuffer *Framebuffer::getColorbuffer()
{
gl::Context *context = gl::getContext();
Colorbuffer *colorbuffer = NULL;
Renderbuffer *rb = mColorbufferPointer.get();
if (mColorbufferType == GL_NONE)
if (rb != NULL && rb->isColorbuffer())
{
UNREACHABLE();
colorbuffer = NULL;
}
else if (mColorbufferType == GL_RENDERBUFFER)
{
colorbuffer = context->getColorbuffer(mColorbufferHandle);
return static_cast<Colorbuffer*>(rb->getStorage());
}
else
{
colorbuffer = context->getTexture(mColorbufferHandle)->getColorbuffer(mColorbufferType);
}
if (colorbuffer && colorbuffer->isColorbuffer())
{
return colorbuffer;
return NULL;
}
return NULL;
}
DepthStencilbuffer *Framebuffer::getDepthbuffer()
{
if (mDepthbufferType != GL_NONE)
{
gl::Context *context = gl::getContext();
DepthStencilbuffer *depthbuffer = context->getDepthbuffer(mDepthbufferHandle);
Renderbuffer *rb = mDepthbufferPointer.get();
if (depthbuffer && depthbuffer->isDepthbuffer())
{
return depthbuffer;
}
if (rb != NULL && rb->isDepthbuffer())
{
return static_cast<DepthStencilbuffer*>(rb->getStorage());
}
else
{
return NULL;
}
return NULL;
}
DepthStencilbuffer *Framebuffer::getStencilbuffer()
{
if (mStencilbufferType != GL_NONE)
{
gl::Context *context = gl::getContext();
DepthStencilbuffer *stencilbuffer = context->getStencilbuffer(mStencilbufferHandle);
Renderbuffer *rb = mStencilbufferPointer.get();
if (stencilbuffer && stencilbuffer->isStencilbuffer())
{
return stencilbuffer;
}
if (rb != NULL && rb->isStencilbuffer())
{
return static_cast<DepthStencilbuffer*>(rb->getStorage());
}
else
{
return NULL;
}
return NULL;
}
GLenum Framebuffer::getColorbufferType()
......@@ -205,114 +211,128 @@ GLenum Framebuffer::getStencilbufferType()
GLuint Framebuffer::getColorbufferHandle()
{
return mColorbufferHandle;
return mColorbufferPointer.id();
}
GLuint Framebuffer::getDepthbufferHandle()
{
return mDepthbufferHandle;
return mDepthbufferPointer.id();
}
GLuint Framebuffer::getStencilbufferHandle()
{
return mStencilbufferHandle;
return mStencilbufferPointer.id();
}
GLenum Framebuffer::completeness()
{
gl::Context *context = gl::getContext();
int width = 0;
int height = 0;
if (mHandle != 0)
if (mColorbufferType != GL_NONE)
{
if (mColorbufferType != GL_NONE)
Colorbuffer *colorbuffer = getColorbuffer();
if (!colorbuffer)
{
Colorbuffer *colorbuffer = getColorbuffer();
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
}
if (!colorbuffer)
{
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
}
if (colorbuffer->getWidth() == 0 || colorbuffer->getHeight() == 0)
{
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
}
if (colorbuffer->getWidth() == 0 || colorbuffer->getHeight() == 0)
{
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
}
width = colorbuffer->getWidth();
height = colorbuffer->getHeight();
}
else
{
return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
}
DepthStencilbuffer *depthbuffer = NULL;
DepthStencilbuffer *stencilbuffer = NULL;
if (mDepthbufferType != GL_NONE)
{
depthbuffer = getDepthbuffer();
width = colorbuffer->getWidth();
height = colorbuffer->getHeight();
if (!depthbuffer)
{
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
}
else
if (depthbuffer->getWidth() == 0 || depthbuffer->getHeight() == 0)
{
return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
}
DepthStencilbuffer *depthbuffer = NULL;
DepthStencilbuffer *stencilbuffer = NULL;
if (width == 0)
{
width = depthbuffer->getWidth();
height = depthbuffer->getHeight();
}
else if (width != depthbuffer->getWidth() || height != depthbuffer->getHeight())
{
return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
}
}
if (mStencilbufferType != GL_NONE)
{
stencilbuffer = getStencilbuffer();
if (mDepthbufferType != GL_NONE)
if (!stencilbuffer)
{
depthbuffer = context->getDepthbuffer(mDepthbufferHandle);
if (!depthbuffer)
{
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
}
if (depthbuffer->getWidth() == 0 || depthbuffer->getHeight() == 0)
{
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
}
if (width == 0)
{
width = depthbuffer->getWidth();
height = depthbuffer->getHeight();
}
else if (width != depthbuffer->getWidth() || height != depthbuffer->getHeight())
{
return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
}
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
}
if (mStencilbufferType != GL_NONE)
if (stencilbuffer->getWidth() == 0 || stencilbuffer->getHeight() == 0)
{
stencilbuffer = context->getStencilbuffer(mStencilbufferHandle);
if (!stencilbuffer)
{
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
}
if (stencilbuffer->getWidth() == 0 || stencilbuffer->getHeight() == 0)
{
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
}
if (width == 0)
{
width = stencilbuffer->getWidth();
height = stencilbuffer->getHeight();
}
else if (width != stencilbuffer->getWidth() || height != stencilbuffer->getHeight())
{
return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
}
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
}
if (mDepthbufferType == GL_RENDERBUFFER && mStencilbufferType == GL_RENDERBUFFER)
if (width == 0)
{
width = stencilbuffer->getWidth();
height = stencilbuffer->getHeight();
}
else if (width != stencilbuffer->getWidth() || height != stencilbuffer->getHeight())
{
return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
}
}
if (mDepthbufferType == GL_RENDERBUFFER && mStencilbufferType == GL_RENDERBUFFER)
{
if (depthbuffer->getFormat() != GL_DEPTH24_STENCIL8_OES ||
stencilbuffer->getFormat() != GL_DEPTH24_STENCIL8_OES ||
depthbuffer->getSerial() != stencilbuffer->getSerial())
{
if (depthbuffer->getFormat() != GL_DEPTH24_STENCIL8_OES ||
stencilbuffer->getFormat() != GL_DEPTH24_STENCIL8_OES ||
depthbuffer->getSerial() != stencilbuffer->getSerial())
{
return GL_FRAMEBUFFER_UNSUPPORTED;
}
return GL_FRAMEBUFFER_UNSUPPORTED;
}
}
return GL_FRAMEBUFFER_COMPLETE;
}
DefaultFramebuffer::DefaultFramebuffer(Colorbuffer *color, DepthStencilbuffer *depthStencil)
{
mColorbufferType = GL_RENDERBUFFER;
mDepthbufferType = GL_RENDERBUFFER;
mStencilbufferType = GL_RENDERBUFFER;
mColorbufferPointer.set(new Renderbuffer(0, color));
Renderbuffer *depthStencilRenderbuffer = new Renderbuffer(0, depthStencil);
mDepthbufferPointer.set(depthStencilRenderbuffer);
mStencilbufferPointer.set(depthStencilRenderbuffer);
}
GLenum DefaultFramebuffer::completeness()
{
return GL_FRAMEBUFFER_COMPLETE;
}
}
......@@ -15,9 +15,11 @@
#include <d3d9.h>
#include "common/angleutils.h"
#include "libGLESv2/RefCountObject.h"
namespace gl
{
class Renderbuffer;
class Colorbuffer;
class Depthbuffer;
class Stencilbuffer;
......@@ -26,9 +28,9 @@ class DepthStencilbuffer;
class Framebuffer
{
public:
explicit Framebuffer(GLuint handle);
Framebuffer();
~Framebuffer();
virtual ~Framebuffer();
void setColorbuffer(GLenum type, GLuint colorbuffer);
void setDepthbuffer(GLenum type, GLuint depthbuffer);
......@@ -55,22 +57,35 @@ class Framebuffer
GLuint getDepthbufferHandle();
GLuint getStencilbufferHandle();
GLenum completeness();
virtual GLenum completeness();
private:
DISALLOW_COPY_AND_ASSIGN(Framebuffer);
GLuint mColorbufferHandle;
protected:
GLenum mColorbufferType;
BindingPointer<Renderbuffer> mColorbufferPointer;
GLuint mDepthbufferHandle;
GLenum mDepthbufferType;
BindingPointer<Renderbuffer> mDepthbufferPointer;
GLuint mStencilbufferHandle;
GLenum mStencilbufferType;
BindingPointer<Renderbuffer> mStencilbufferPointer;
private:
DISALLOW_COPY_AND_ASSIGN(Framebuffer);
Renderbuffer *lookupRenderbuffer(GLenum type, GLuint handle) const;
};
class DefaultFramebuffer : public Framebuffer
{
public:
DefaultFramebuffer(Colorbuffer *color, DepthStencilbuffer *depthStencil);
GLuint mHandle;
virtual GLenum completeness();
private:
DISALLOW_COPY_AND_ASSIGN(DefaultFramebuffer);
};
}
#endif // LIBGLESV2_FRAMEBUFFER_H_
//
// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// RefCountObject.cpp: Defines the gl::RefCountObject base class that provides
// lifecycle support for GL objects using the traditional BindObject scheme, but
// that need to be reference counted for correct cross-context deletion.
// (Concretely, textures, buffers and renderbuffers.)
#include "RefCountObject.h"
namespace gl
{
RefCountObject::RefCountObject(GLuint id)
{
mId = id;
mRefCount = 0;
}
RefCountObject::~RefCountObject()
{
}
void RefCountObject::addRef() const
{
mRefCount++;
}
void RefCountObject::release() const
{
ASSERT(mRefCount > 0);
if (--mRefCount == 0)
{
delete this;
}
}
void RefCountObjectBindingPointer::set(RefCountObject *newObject)
{
// addRef first in case newObject == mObject and this is the last reference to it.
if (newObject != NULL) newObject->addRef();
if (mObject != NULL) mObject->release();
mObject = newObject;
}
}
//
// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// RefCountObject.h: Defines the gl::RefCountObject base class that provides
// lifecycle support for GL objects using the traditional BindObject scheme, but
// that need to be reference counted for correct cross-context deletion.
// (Concretely, textures, buffers and renderbuffers.)
#ifndef LIBGLESV2_REFCOUNTOBJECT_H_
#define LIBGLESV2_REFCOUNTOBJECT_H_
#include <cstddef>
#define GL_APICALL
#include <GLES2/gl2.h>
#include "common/debug.h"
namespace gl
{
class RefCountObject
{
public:
explicit RefCountObject(GLuint id);
virtual ~RefCountObject();
virtual void addRef() const;
virtual void release() const;
GLuint id() const { return mId; }
private:
GLuint mId;
mutable std::size_t mRefCount;
};
class RefCountObjectBindingPointer
{
protected:
RefCountObjectBindingPointer() : mObject(NULL) { }
~RefCountObjectBindingPointer() { ASSERT(mObject == NULL); } // Objects have to be released before the resource manager is destroyed, so they must be explicitly cleaned up.
void set(RefCountObject *newObject);
RefCountObject *get() const { return mObject; }
public:
GLuint id() const { return (mObject != NULL) ? mObject->id() : 0; }
bool operator ! () const { return (get() == NULL); }
private:
RefCountObject *mObject;
};
template <class ObjectType>
class BindingPointer : public RefCountObjectBindingPointer
{
public:
void set(ObjectType *newObject) { RefCountObjectBindingPointer::set(newObject); }
ObjectType *get() const { return static_cast<ObjectType*>(RefCountObjectBindingPointer::get()); }
ObjectType *operator -> () const { return get(); }
};
}
#endif // LIBGLESV2_REFCOUNTOBJECT_H_
......@@ -15,73 +15,133 @@
namespace gl
{
unsigned int Renderbuffer::mCurrentSerial = 1;
unsigned int RenderbufferStorage::mCurrentSerial = 1;
Renderbuffer::Renderbuffer()
Renderbuffer::Renderbuffer(GLuint id, RenderbufferStorage *storage) : RefCountObject(id)
{
mWidth = 0;
mHeight = 0;
mFormat = GL_RGBA4; // default format, needs to be one of the expected renderbuffer formats
mSerial = issueSerial();
ASSERT(storage != NULL);
mStorage = storage;
}
Renderbuffer::~Renderbuffer()
{
delete mStorage;
}
bool Renderbuffer::isColorbuffer() const
{
return mStorage->isColorbuffer();
}
bool Renderbuffer::isDepthbuffer() const
{
return mStorage->isDepthbuffer();
}
bool Renderbuffer::isColorbuffer()
bool Renderbuffer::isStencilbuffer() const
{
return mStorage->isStencilbuffer();
}
IDirect3DSurface9 *Renderbuffer::getRenderTarget()
{
return mStorage->getRenderTarget();
}
IDirect3DSurface9 *Renderbuffer::getDepthStencil()
{
return mStorage->getDepthStencil();
}
int Renderbuffer::getWidth() const
{
return mStorage->getWidth();
}
int Renderbuffer::getHeight() const
{
return mStorage->getHeight();
}
GLenum Renderbuffer::getFormat() const
{
return mStorage->getFormat();
}
unsigned int Renderbuffer::getSerial() const
{
return mStorage->getSerial();
}
void Renderbuffer::setStorage(RenderbufferStorage *newStorage)
{
ASSERT(newStorage != NULL);
delete mStorage;
mStorage = newStorage;
}
RenderbufferStorage::RenderbufferStorage()
{
mSerial = issueSerial();
}
RenderbufferStorage::~RenderbufferStorage()
{
}
bool RenderbufferStorage::isColorbuffer() const
{
return false;
}
bool Renderbuffer::isDepthbuffer()
bool RenderbufferStorage::isDepthbuffer() const
{
return false;
}
bool Renderbuffer::isStencilbuffer()
bool RenderbufferStorage::isStencilbuffer() const
{
return false;
}
IDirect3DSurface9 *Renderbuffer::getRenderTarget()
IDirect3DSurface9 *RenderbufferStorage::getRenderTarget()
{
return NULL;
}
IDirect3DSurface9 *Renderbuffer::getDepthStencil()
IDirect3DSurface9 *RenderbufferStorage::getDepthStencil()
{
return NULL;
}
int Renderbuffer::getWidth()
int RenderbufferStorage::getWidth() const
{
return mWidth;
}
int Renderbuffer::getHeight()
int RenderbufferStorage::getHeight() const
{
return mHeight;
}
void Renderbuffer::setSize(int width, int height)
void RenderbufferStorage::setSize(int width, int height)
{
mWidth = width;
mHeight = height;
}
GLenum Renderbuffer::getFormat()
GLenum RenderbufferStorage::getFormat() const
{
return mFormat;
}
unsigned int Renderbuffer::getSerial() const
unsigned int RenderbufferStorage::getSerial() const
{
return mSerial;
}
unsigned int Renderbuffer::issueSerial()
unsigned int RenderbufferStorage::issueSerial()
{
return mCurrentSerial++;
}
......@@ -105,17 +165,21 @@ Colorbuffer::Colorbuffer(int width, int height, GLenum format)
IDirect3DDevice9 *device = getDevice();
mRenderTarget = NULL;
HRESULT result = device->CreateRenderTarget(width, height, es2dx::ConvertRenderbufferFormat(format),
D3DMULTISAMPLE_NONE, 0, FALSE, &mRenderTarget, NULL);
if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
if (width > 0 && height > 0)
{
error(GL_OUT_OF_MEMORY);
HRESULT result = device->CreateRenderTarget(width, height, es2dx::ConvertRenderbufferFormat(format),
D3DMULTISAMPLE_NONE, 0, FALSE, &mRenderTarget, NULL);
return;
}
if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
{
error(GL_OUT_OF_MEMORY);
ASSERT(SUCCEEDED(result));
return;
}
ASSERT(SUCCEEDED(result));
}
if (mRenderTarget)
{
......@@ -137,12 +201,12 @@ Colorbuffer::~Colorbuffer()
}
}
bool Colorbuffer::isColorbuffer()
bool Colorbuffer::isColorbuffer() const
{
return true;
}
GLuint Colorbuffer::getRedSize()
GLuint Colorbuffer::getRedSize() const
{
if (mRenderTarget)
{
......@@ -155,7 +219,7 @@ GLuint Colorbuffer::getRedSize()
return 0;
}
GLuint Colorbuffer::getGreenSize()
GLuint Colorbuffer::getGreenSize() const
{
if (mRenderTarget)
{
......@@ -168,7 +232,7 @@ GLuint Colorbuffer::getGreenSize()
return 0;
}
GLuint Colorbuffer::getBlueSize()
GLuint Colorbuffer::getBlueSize() const
{
if (mRenderTarget)
{
......@@ -181,7 +245,7 @@ GLuint Colorbuffer::getBlueSize()
return 0;
}
GLuint Colorbuffer::getAlphaSize()
GLuint Colorbuffer::getAlphaSize() const
{
if (mRenderTarget)
{
......@@ -249,17 +313,17 @@ DepthStencilbuffer::~DepthStencilbuffer()
}
}
bool DepthStencilbuffer::isDepthbuffer()
bool DepthStencilbuffer::isDepthbuffer() const
{
return true;
}
bool DepthStencilbuffer::isStencilbuffer()
bool DepthStencilbuffer::isStencilbuffer() const
{
return true;
}
GLuint DepthStencilbuffer::getDepthSize()
GLuint DepthStencilbuffer::getDepthSize() const
{
if (mDepthStencil)
{
......@@ -272,7 +336,7 @@ GLuint DepthStencilbuffer::getDepthSize()
return 0;
}
GLuint DepthStencilbuffer::getStencilSize()
GLuint DepthStencilbuffer::getStencilSize() const
{
if (mDepthStencil)
{
......@@ -314,12 +378,12 @@ Depthbuffer::~Depthbuffer()
{
}
bool Depthbuffer::isDepthbuffer()
bool Depthbuffer::isDepthbuffer() const
{
return true;
}
bool Depthbuffer::isStencilbuffer()
bool Depthbuffer::isStencilbuffer() const
{
return false;
}
......@@ -352,12 +416,12 @@ Stencilbuffer::~Stencilbuffer()
{
}
bool Stencilbuffer::isDepthbuffer()
bool Stencilbuffer::isDepthbuffer() const
{
return false;
}
bool Stencilbuffer::isStencilbuffer()
bool Stencilbuffer::isStencilbuffer() const
{
return true;
}
......
......@@ -4,8 +4,9 @@
// found in the LICENSE file.
//
// Renderbuffer.h: Defines the virtual gl::Renderbuffer class and its derived
// classes Colorbuffer, Depthbuffer and Stencilbuffer. Implements GL renderbuffer
// Renderbuffer.h: Defines the wrapper class gl::Renderbuffer, as well as the
// class hierarchy used to store its contents: RenderbufferStorage, Colorbuffer,
// DepthStencilbuffer, Depthbuffer and Stencilbuffer. Implements GL renderbuffer
// objects and related functionality. [OpenGL ES 2.0.24] section 4.4.3 page 108.
#ifndef LIBGLESV2_RENDERBUFFER_H_
......@@ -16,26 +17,31 @@
#include <d3d9.h>
#include "common/angleutils.h"
#include "libGLESv2/RefCountObject.h"
namespace gl
{
class Renderbuffer
// A class derived from RenderbufferStorage is created whenever glRenderbufferStorage
// is called. The specific concrete type depends on whether the internal format is
// colour depth, stencil or packed depth/stencil.
class RenderbufferStorage
{
public:
Renderbuffer();
RenderbufferStorage();
virtual ~Renderbuffer();
virtual ~RenderbufferStorage() = 0;
virtual bool isColorbuffer();
virtual bool isDepthbuffer();
virtual bool isStencilbuffer();
virtual bool isColorbuffer() const;
virtual bool isDepthbuffer() const;
virtual bool isStencilbuffer() const;
virtual IDirect3DSurface9 *getRenderTarget();
virtual IDirect3DSurface9 *getDepthStencil();
virtual int getWidth();
virtual int getHeight();
GLenum getFormat();
virtual int getWidth() const;
virtual int getHeight() const;
GLenum getFormat() const;
unsigned int getSerial() const;
static unsigned int issueSerial();
......@@ -46,7 +52,7 @@ class Renderbuffer
unsigned int mSerial;
private:
DISALLOW_COPY_AND_ASSIGN(Renderbuffer);
DISALLOW_COPY_AND_ASSIGN(RenderbufferStorage);
static unsigned int mCurrentSerial;
......@@ -54,7 +60,38 @@ class Renderbuffer
int mHeight;
};
class Colorbuffer : public Renderbuffer
// Renderbuffer implements the GL renderbuffer object.
// It's only a wrapper for a RenderbufferStorage, but the internal object
// can change whenever glRenderbufferStorage is called.
class Renderbuffer : public RefCountObject
{
public:
Renderbuffer(GLuint id, RenderbufferStorage *storage);
~Renderbuffer();
bool isColorbuffer() const;
bool isDepthbuffer() const;
bool isStencilbuffer() const;
IDirect3DSurface9 *getRenderTarget();
IDirect3DSurface9 *getDepthStencil();
int getWidth() const;
int getHeight() const;
GLenum getFormat() const;
unsigned int getSerial() const;
void setStorage(RenderbufferStorage *newStorage);
RenderbufferStorage *getStorage() { return mStorage; }
private:
DISALLOW_COPY_AND_ASSIGN(Renderbuffer);
RenderbufferStorage *mStorage;
};
class Colorbuffer : public RenderbufferStorage
{
public:
explicit Colorbuffer(IDirect3DSurface9 *renderTarget);
......@@ -62,12 +99,12 @@ class Colorbuffer : public Renderbuffer
~Colorbuffer();
bool isColorbuffer();
bool isColorbuffer() const;
GLuint getRedSize();
GLuint getGreenSize();
GLuint getBlueSize();
GLuint getAlphaSize();
GLuint getRedSize() const;
GLuint getGreenSize() const;
GLuint getBlueSize() const;
GLuint getAlphaSize() const;
IDirect3DSurface9 *getRenderTarget();
......@@ -78,7 +115,7 @@ class Colorbuffer : public Renderbuffer
DISALLOW_COPY_AND_ASSIGN(Colorbuffer);
};
class DepthStencilbuffer : public Renderbuffer
class DepthStencilbuffer : public RenderbufferStorage
{
public:
explicit DepthStencilbuffer(IDirect3DSurface9 *depthStencil);
......@@ -86,11 +123,11 @@ class DepthStencilbuffer : public Renderbuffer
~DepthStencilbuffer();
virtual bool isDepthbuffer();
virtual bool isStencilbuffer();
virtual bool isDepthbuffer() const;
virtual bool isStencilbuffer() const;
GLuint getDepthSize();
GLuint getStencilSize();
GLuint getDepthSize() const;
GLuint getStencilSize() const;
IDirect3DSurface9 *getDepthStencil();
......@@ -107,8 +144,8 @@ class Depthbuffer : public DepthStencilbuffer
~Depthbuffer();
bool isDepthbuffer();
bool isStencilbuffer();
bool isDepthbuffer() const;
bool isStencilbuffer() const;
private:
DISALLOW_COPY_AND_ASSIGN(Depthbuffer);
......@@ -122,8 +159,8 @@ class Stencilbuffer : public DepthStencilbuffer
~Stencilbuffer();
bool isDepthbuffer();
bool isStencilbuffer();
bool isDepthbuffer() const;
bool isStencilbuffer() const;
private:
DISALLOW_COPY_AND_ASSIGN(Stencilbuffer);
......
......@@ -146,14 +146,13 @@ GLuint ResourceManager::createRenderbuffer()
return handle;
}
// FIXME: shared object deletion needs handling
void ResourceManager::deleteBuffer(GLuint buffer)
{
BufferMap::iterator bufferObject = mBufferMap.find(buffer);
if (bufferObject != mBufferMap.end())
{
delete bufferObject->second;
if (bufferObject->second) bufferObject->second->release();
mBufferMap.erase(bufferObject);
}
}
......@@ -194,30 +193,24 @@ void ResourceManager::deleteProgram(GLuint program)
}
}
// FIXME: shared object deletion needs handling
void ResourceManager::deleteTexture(GLuint texture)
{
TextureMap::iterator textureObject = mTextureMap.find(texture);
if (textureObject != mTextureMap.end())
{
if (texture != 0)
{
delete textureObject->second;
}
if (textureObject->second) textureObject->second->release();
mTextureMap.erase(textureObject);
}
}
// FIXME: shared object deletion needs handling
void ResourceManager::deleteRenderbuffer(GLuint renderbuffer)
{
RenderbufferMap::iterator renderbufferObject = mRenderbufferMap.find(renderbuffer);
if (renderbufferObject != mRenderbufferMap.end())
{
delete renderbufferObject->second;
if (renderbufferObject->second) renderbufferObject->second->release();
mRenderbufferMap.erase(renderbufferObject);
}
}
......@@ -303,7 +296,9 @@ void ResourceManager::checkBufferAllocation(unsigned int buffer)
{
if (buffer != 0 && !getBuffer(buffer))
{
mBufferMap[buffer] = new Buffer();
Buffer *bufferObject = new Buffer(buffer);
mBufferMap[buffer] = bufferObject;
bufferObject->addRef();
}
}
......@@ -311,14 +306,24 @@ void ResourceManager::checkTextureAllocation(GLuint texture, SamplerType type)
{
if (!getTexture(texture) && texture != 0)
{
Texture *textureObject;
if (type == SAMPLER_2D)
{
mTextureMap[texture] = new Texture2D();
textureObject = new Texture2D(texture);
}
else if (type == SAMPLER_CUBE)
{
mTextureMap[texture] = new TextureCubeMap();
textureObject = new TextureCubeMap(texture);
}
else
{
UNREACHABLE();
return;
}
mTextureMap[texture] = textureObject;
textureObject->addRef();
}
}
......@@ -326,7 +331,9 @@ void ResourceManager::checkRenderbufferAllocation(GLuint renderbuffer)
{
if (renderbuffer != 0 && !getRenderbuffer(renderbuffer))
{
mRenderbufferMap[renderbuffer] = new Renderbuffer();
Renderbuffer *renderbufferObject = new Renderbuffer(renderbuffer, new Colorbuffer(0, 0, GL_RGBA4));
mRenderbufferMap[renderbuffer] = renderbufferObject;
renderbufferObject->addRef();
}
}
......
......@@ -32,7 +32,7 @@ Texture::Image::~Image()
if (surface) surface->Release();
}
Texture::Texture()
Texture::Texture(GLuint id) : RefCountObject(id)
{
mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
mMagFilter = GL_LINEAR;
......@@ -443,7 +443,7 @@ int Texture::levelCount() const
return mBaseTexture ? mBaseTexture->GetLevelCount() : 0;
}
Texture2D::Texture2D()
Texture2D::Texture2D(GLuint id) : Texture(id)
{
mTexture = NULL;
mColorbufferProxy = NULL;
......@@ -564,7 +564,7 @@ void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei widt
commitRect(level, xoffset, yoffset, width, height);
}
void Texture2D::copyImage(GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
void Texture2D::copyImage(GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source)
{
if (redefineTexture(level, internalFormat, width, height))
{
......@@ -592,7 +592,7 @@ void Texture2D::copyImage(GLint level, GLenum internalFormat, GLint x, GLint y,
mImageArray[level].format = internalFormat;
}
void Texture2D::copySubImage(GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
void Texture2D::copySubImage(GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source)
{
if (xoffset + width > mImageArray[level].width || yoffset + height > mImageArray[level].height)
{
......@@ -872,16 +872,17 @@ void Texture2D::generateMipmaps()
}
}
Colorbuffer *Texture2D::getColorbuffer(GLenum target)
Renderbuffer *Texture2D::getColorbuffer(GLenum target)
{
if (target != GL_TEXTURE_2D)
{
return error(GL_INVALID_OPERATION, (Colorbuffer *)NULL);
return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
}
if (mColorbufferProxy == NULL)
{
mColorbufferProxy = new TextureColorbufferProxy(this, target);
mColorbufferProxy = new Renderbuffer(id(), new TextureColorbufferProxy(this, target));
mColorbufferProxy->addRef();
}
return mColorbufferProxy;
......@@ -899,7 +900,7 @@ IDirect3DSurface9 *Texture2D::getRenderTarget(GLenum target)
return renderTarget;
}
TextureCubeMap::TextureCubeMap()
TextureCubeMap::TextureCubeMap(GLuint id) : Texture(id)
{
mTexture = NULL;
......@@ -1279,7 +1280,7 @@ bool TextureCubeMap::redefineTexture(GLint level, GLenum internalFormat, GLsizei
return !textureOkay;
}
void TextureCubeMap::copyImage(GLenum face, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
void TextureCubeMap::copyImage(GLenum face, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source)
{
unsigned int faceindex = faceIndex(face);
......@@ -1341,7 +1342,7 @@ IDirect3DSurface9 *TextureCubeMap::getCubeMapSurface(unsigned int faceIdentifier
return (SUCCEEDED(hr)) ? surface : NULL;
}
void TextureCubeMap::copySubImage(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
void TextureCubeMap::copySubImage(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source)
{
GLsizei size = mImageArray[faceIndex(face)][level].width;
......@@ -1441,18 +1442,19 @@ void TextureCubeMap::generateMipmaps()
}
}
Colorbuffer *TextureCubeMap::getColorbuffer(GLenum target)
Renderbuffer *TextureCubeMap::getColorbuffer(GLenum target)
{
if (!IsCubemapTextureTarget(target))
{
return error(GL_INVALID_OPERATION, (Colorbuffer *)NULL);
return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
}
unsigned int face = faceIndex(target);
if (mFaceProxies[face] == NULL)
{
mFaceProxies[face] = new TextureColorbufferProxy(this, target);
mFaceProxies[face] = new Renderbuffer(id(), new TextureColorbufferProxy(this, target));
mFaceProxies[face]->addRef();
}
return mFaceProxies[face];
......@@ -1476,6 +1478,16 @@ Texture::TextureColorbufferProxy::TextureColorbufferProxy(Texture *texture, GLen
ASSERT(target == GL_TEXTURE_2D || IsCubemapTextureTarget(target));
}
void Texture::TextureColorbufferProxy::addRef() const
{
mTexture->addRef();
}
void Texture::TextureColorbufferProxy::release() const
{
mTexture->release();
}
IDirect3DSurface9 *Texture::TextureColorbufferProxy::getRenderTarget()
{
if (mRenderTarget) mRenderTarget->Release();
......@@ -1485,12 +1497,12 @@ IDirect3DSurface9 *Texture::TextureColorbufferProxy::getRenderTarget()
return mRenderTarget;
}
int Texture::TextureColorbufferProxy::getWidth()
int Texture::TextureColorbufferProxy::getWidth() const
{
return mTexture->getWidth();
}
int Texture::TextureColorbufferProxy::getHeight()
int Texture::TextureColorbufferProxy::getHeight() const
{
return mTexture->getHeight();
}
......
......@@ -33,10 +33,10 @@ enum
MAX_TEXTURE_LEVELS = 12 // 1+log2 of MAX_TEXTURE_SIZE
};
class Texture
class Texture : public RefCountObject
{
public:
explicit Texture();
explicit Texture(GLuint id);
virtual ~Texture();
......@@ -58,12 +58,14 @@ class Texture
virtual bool isComplete() const = 0;
IDirect3DBaseTexture9 *getTexture();
virtual Colorbuffer *getColorbuffer(GLenum target) = 0;
virtual Renderbuffer *getColorbuffer(GLenum target) = 0;
virtual void generateMipmaps() = 0;
bool isDirty() const;
static const GLuint INCOMPLETE_TEXTURE_ID = static_cast<GLuint>(-1); // Every texture takes an id at creation time. The value is arbitrary because it is never registered with the resource manager.
protected:
class TextureColorbufferProxy;
friend class TextureColorbufferProxy;
......@@ -73,10 +75,13 @@ class Texture
TextureColorbufferProxy(Texture *texture, GLenum target);
// target is a 2D-like texture target (GL_TEXTURE_2D or one of the cube face targets)
virtual void addRef() const;
virtual void release() const;
virtual IDirect3DSurface9 *getRenderTarget();
virtual int getWidth();
virtual int getHeight();
virtual int getWidth() const;
virtual int getHeight() const;
private:
Texture *mTexture;
......@@ -148,7 +153,7 @@ class Texture
class Texture2D : public Texture
{
public:
explicit Texture2D();
explicit Texture2D(GLuint id);
~Texture2D();
......@@ -156,14 +161,14 @@ class Texture2D : public Texture
void setImage(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels);
void subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels);
void copyImage(GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source);
void copySubImage(GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source);
void copyImage(GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source);
void copySubImage(GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source);
bool isComplete() const;
virtual void generateMipmaps();
virtual Colorbuffer *getColorbuffer(GLenum target);
virtual Renderbuffer *getColorbuffer(GLenum target);
private:
DISALLOW_COPY_AND_ASSIGN(Texture2D);
......@@ -180,7 +185,7 @@ class Texture2D : public Texture
IDirect3DTexture9 *mTexture;
TextureColorbufferProxy *mColorbufferProxy;
Renderbuffer *mColorbufferProxy;
bool redefineTexture(GLint level, GLenum internalFormat, GLsizei width, GLsizei height);
......@@ -190,7 +195,7 @@ class Texture2D : public Texture
class TextureCubeMap : public Texture
{
public:
explicit TextureCubeMap();
explicit TextureCubeMap(GLuint id);
~TextureCubeMap();
......@@ -204,14 +209,14 @@ class TextureCubeMap : public Texture
void setImageNegZ(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels);
void subImage(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels);
void copyImage(GLenum face, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source);
void copySubImage(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source);
void copyImage(GLenum face, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source);
void copySubImage(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source);
bool isComplete() const;
virtual void generateMipmaps();
virtual Colorbuffer *getColorbuffer(GLenum target);
virtual Renderbuffer *getColorbuffer(GLenum target);
private:
DISALLOW_COPY_AND_ASSIGN(TextureCubeMap);
......@@ -238,7 +243,7 @@ class TextureCubeMap : public Texture
IDirect3DCubeTexture9 *mTexture;
TextureColorbufferProxy *mFaceProxies[6];
Renderbuffer *mFaceProxies[6];
virtual IDirect3DSurface9 *getRenderTarget(GLenum target);
};
......
......@@ -128,9 +128,9 @@ GLenum VertexDataManager::preRenderValidate(GLint start, GLsizei count,
void *output = mStreamBuffer->map(spaceRequired(attribs[i], count), &translated[i].offset);
const void *input;
if (attribs[i].mBoundBuffer)
if (attribs[i].mBoundBuffer.get())
{
Buffer *buffer = mContext->getBuffer(attribs[i].mBoundBuffer);
Buffer *buffer = attribs[i].mBoundBuffer.get();
size_t offset = reinterpret_cast<size_t>(attribs[i].mPointer);
......
......@@ -854,8 +854,7 @@ void __stdcall glCopyTexImage2D(GLenum target, GLint level, GLenum internalforma
return error(GL_INVALID_FRAMEBUFFER_OPERATION);
}
gl::Renderbuffer *source = framebuffer->getColorbuffer();
gl::Colorbuffer *source = framebuffer->getColorbuffer();
if (target == GL_TEXTURE_2D)
{
gl::Texture2D *texture = context->getTexture2D();
......@@ -928,8 +927,7 @@ void __stdcall glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GL
return error(GL_INVALID_FRAMEBUFFER_OPERATION);
}
gl::Renderbuffer *source = framebuffer->getColorbuffer();
gl::Colorbuffer *source = framebuffer->getColorbuffer();
if (target == GL_TEXTURE_2D)
{
gl::Texture2D *texture = context->getTexture2D();
......@@ -2520,7 +2518,7 @@ void __stdcall glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint*
case GL_RENDERBUFFER_RED_SIZE:
if (renderbuffer->isColorbuffer())
{
*params = static_cast<gl::Colorbuffer*>(renderbuffer)->getRedSize();
*params = static_cast<gl::Colorbuffer*>(renderbuffer->getStorage())->getRedSize();
}
else
{
......@@ -2530,7 +2528,7 @@ void __stdcall glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint*
case GL_RENDERBUFFER_GREEN_SIZE:
if (renderbuffer->isColorbuffer())
{
*params = static_cast<gl::Colorbuffer*>(renderbuffer)->getGreenSize();
*params = static_cast<gl::Colorbuffer*>(renderbuffer->getStorage())->getGreenSize();
}
else
{
......@@ -2540,7 +2538,7 @@ void __stdcall glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint*
case GL_RENDERBUFFER_BLUE_SIZE:
if (renderbuffer->isColorbuffer())
{
*params = static_cast<gl::Colorbuffer*>(renderbuffer)->getBlueSize();
*params = static_cast<gl::Colorbuffer*>(renderbuffer->getStorage())->getBlueSize();
}
else
{
......@@ -2550,7 +2548,7 @@ void __stdcall glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint*
case GL_RENDERBUFFER_ALPHA_SIZE:
if (renderbuffer->isColorbuffer())
{
*params = static_cast<gl::Colorbuffer*>(renderbuffer)->getAlphaSize();
*params = static_cast<gl::Colorbuffer*>(renderbuffer->getStorage())->getAlphaSize();
}
else
{
......@@ -2560,7 +2558,7 @@ void __stdcall glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint*
case GL_RENDERBUFFER_DEPTH_SIZE:
if (renderbuffer->isDepthbuffer())
{
*params = static_cast<gl::Depthbuffer*>(renderbuffer)->getDepthSize();
*params = static_cast<gl::Depthbuffer*>(renderbuffer->getStorage())->getDepthSize();
}
else
{
......@@ -2570,7 +2568,7 @@ void __stdcall glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint*
case GL_RENDERBUFFER_STENCIL_SIZE:
if (renderbuffer->isStencilbuffer())
{
*params = static_cast<gl::Stencilbuffer*>(renderbuffer)->getStencilSize();
*params = static_cast<gl::Stencilbuffer*>(renderbuffer->getStorage())->getStencilSize();
}
else
{
......@@ -3005,7 +3003,7 @@ void __stdcall glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params)
return error(GL_INVALID_VALUE);
}
gl::AttributeState attribState = context->getVertexAttribState(index);
const gl::AttributeState &attribState = context->getVertexAttribState(index);
switch (pname)
{
......@@ -3025,7 +3023,7 @@ void __stdcall glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params)
*params = (GLfloat)(attribState.mNormalized ? GL_TRUE : GL_FALSE);
break;
case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
*params = (GLfloat)attribState.mBoundBuffer;
*params = (GLfloat)attribState.mBoundBuffer.id();
break;
case GL_CURRENT_VERTEX_ATTRIB:
for (int i = 0; i < 4; ++i)
......@@ -3058,7 +3056,7 @@ void __stdcall glGetVertexAttribiv(GLuint index, GLenum pname, GLint* params)
return error(GL_INVALID_VALUE);
}
gl::AttributeState attribState = context->getVertexAttribState(index);
const gl::AttributeState &attribState = context->getVertexAttribState(index);
switch (pname)
{
......@@ -3078,7 +3076,7 @@ void __stdcall glGetVertexAttribiv(GLuint index, GLenum pname, GLint* params)
*params = (attribState.mNormalized ? GL_TRUE : GL_FALSE);
break;
case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
*params = attribState.mBoundBuffer;
*params = attribState.mBoundBuffer.id();
break;
case GL_CURRENT_VERTEX_ATTRIB:
for (int i = 0; i < 4; ++i)
......@@ -3570,7 +3568,8 @@ void __stdcall glRenderbufferStorage(GLenum target, GLenum internalformat, GLsiz
if (context)
{
if (context->getRenderbufferHandle() == 0)
GLuint handle = context->getRenderbufferHandle();
if (handle == 0)
{
return error(GL_INVALID_OPERATION);
}
......@@ -3578,18 +3577,18 @@ void __stdcall glRenderbufferStorage(GLenum target, GLenum internalformat, GLsiz
switch (internalformat)
{
case GL_DEPTH_COMPONENT16:
context->setRenderbuffer(new gl::Depthbuffer(width, height));
context->setRenderbufferStorage(new gl::Depthbuffer(width, height));
break;
case GL_RGBA4:
case GL_RGB5_A1:
case GL_RGB565:
context->setRenderbuffer(new gl::Colorbuffer(width, height, internalformat));
context->setRenderbufferStorage(new gl::Colorbuffer(width, height, internalformat));
break;
case GL_STENCIL_INDEX8:
context->setRenderbuffer(new gl::Stencilbuffer(width, height));
context->setRenderbufferStorage(new gl::Stencilbuffer(width, height));
break;
case GL_DEPTH24_STENCIL8_OES:
context->setRenderbuffer(new gl::DepthStencilbuffer(width, height));
context->setRenderbufferStorage(new gl::DepthStencilbuffer(width, height));
break;
default:
return error(GL_INVALID_ENUM);
......@@ -4981,7 +4980,7 @@ void __stdcall glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLbo
if (context)
{
context->setVertexAttribState(index, context->getArrayBufferHandle(), size, type, (normalized == GL_TRUE), stride, ptr);
context->setVertexAttribState(index, context->getArrayBuffer(), size, type, (normalized == GL_TRUE), stride, ptr);
}
}
catch(std::bad_alloc&)
......
......@@ -217,6 +217,10 @@
>
</File>
<File
RelativePath=".\RefCountObject.cpp"
>
</File>
<File
RelativePath=".\Renderbuffer.cpp"
>
</File>
......@@ -303,6 +307,10 @@
>
</File>
<File
RelativePath=".\RefCountObject.h"
>
</File>
<File
RelativePath=".\Renderbuffer.h"
>
</File>
......
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