Implement support for mipmap generation

TRAC #11338 Signed-off-by: Nicolas Capens Signed-off-by: Daniel Koch Author: Andrew Lewycky git-svn-id: https://angleproject.googlecode.com/svn/trunk@161 736b8ea6-26fd-11df-bfd4-992fa37f6226
parent 5d2bee93
......@@ -238,6 +238,48 @@ bool Blit::setPixelShader(ShaderId shader)
return setShader<IDirect3DPixelShader9>(shader, mContext->getPixelShaderProfile(), &IDirect3DDevice9::CreatePixelShader, &IDirect3DDevice9::SetPixelShader);
}
RECT Blit::getSurfaceRect(IDirect3DSurface9 *surface) const
{
D3DSURFACE_DESC desc;
surface->GetDesc(&desc);
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = desc.Width;
rect.bottom = desc.Height;
return rect;
}
bool Blit::boxFilter(IDirect3DSurface9 *source, IDirect3DSurface9 *dest)
{
IDirect3DTexture9 *texture = copySurfaceToTexture(source, getSurfaceRect(source));
if (!texture)
{
return false;
}
IDirect3DDevice9 *device = getDevice();
device->SetTexture(0, texture);
device->SetRenderTarget(0, dest);
setVertexShader(SHADER_VS_STANDARD);
setPixelShader(SHADER_PS_PASSTHROUGH);
setCommonBlitState();
device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
setViewport(getSurfaceRect(dest), 0, 0);
render();
texture->Release();
return true;
}
bool Blit::formatConvert(IDirect3DSurface9 *source, const RECT &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, IDirect3DSurface9 *dest)
{
IDirect3DTexture9 *texture = copySurfaceToTexture(source, sourceRect);
......@@ -251,7 +293,7 @@ bool Blit::formatConvert(IDirect3DSurface9 *source, const RECT &sourceRect, GLen
device->SetTexture(0, texture);
device->SetRenderTarget(0, dest);
setViewport(sourceRect, xoffset, yoffset, dest);
setViewport(sourceRect, xoffset, yoffset);
setCommonBlitState();
if (setFormatConvertShaders(destFormat))
......@@ -334,7 +376,7 @@ IDirect3DTexture9 *Blit::copySurfaceToTexture(IDirect3DSurface9 *surface, const
// Copy the render target into a texture
IDirect3DTexture9 *texture;
HRESULT result = device->CreateTexture(sourceRect.right - sourceRect.left, sourceRect.top - sourceRect.bottom, 1, D3DUSAGE_RENDERTARGET, sourceDesc.Format, D3DPOOL_DEFAULT, &texture, NULL);
HRESULT result = device->CreateTexture(sourceRect.right - sourceRect.left, sourceRect.bottom - sourceRect.top, 1, D3DUSAGE_RENDERTARGET, sourceDesc.Format, D3DPOOL_DEFAULT, &texture, NULL);
if (FAILED(result))
{
......@@ -355,8 +397,8 @@ IDirect3DTexture9 *Blit::copySurfaceToTexture(IDirect3DSurface9 *surface, const
RECT d3dSourceRect;
d3dSourceRect.left = sourceRect.left;
d3dSourceRect.right = sourceRect.right;
d3dSourceRect.top = sourceRect.bottom;
d3dSourceRect.bottom = sourceRect.top;
d3dSourceRect.top = sourceRect.top;
d3dSourceRect.bottom = sourceRect.bottom;
result = device->StretchRect(surface, &d3dSourceRect, textureSurface, NULL, D3DTEXF_NONE);
......@@ -372,18 +414,15 @@ IDirect3DTexture9 *Blit::copySurfaceToTexture(IDirect3DSurface9 *surface, const
return texture;
}
void Blit::setViewport(const RECT &sourceRect, GLint xoffset, GLint yoffset, IDirect3DSurface9 *dest)
void Blit::setViewport(const RECT &sourceRect, GLint xoffset, GLint yoffset)
{
D3DSURFACE_DESC desc;
dest->GetDesc(&desc);
IDirect3DDevice9 *device = getDevice();
D3DVIEWPORT9 vp;
vp.X = xoffset;
vp.Y = yoffset;
vp.Width = sourceRect.right - sourceRect.left;
vp.Height = sourceRect.top - sourceRect.bottom;
vp.Height = sourceRect.bottom - sourceRect.top;
vp.MinZ = 0.0f;
vp.MaxZ = 1.0f;
device->SetViewport(&vp);
......@@ -410,6 +449,8 @@ void Blit::setCommonBlitState()
device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
device->SetSamplerState(0, D3DSAMP_SRGBTEXTURE, FALSE);
device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
}
void Blit::render()
......
......@@ -29,10 +29,14 @@ class Blit
~Blit();
// Copy from source surface to dest surface.
// sourceRect, xoffset, yoffset are in OpenGL coordinates. Assumes that source is internally flipped (for example as a render target would be).
// sourceRect, xoffset, yoffset are in D3D coordinates (0,0 in upper-left)
// source is interpreted as RGBA and destFormat specifies the desired result format. For example, if destFormat = GL_RGB, the alpha channel will be forced to 0.
bool formatConvert(IDirect3DSurface9 *source, const RECT &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, IDirect3DSurface9 *dest);
// 2x2 box filter sample from source to dest.
// Requires that source is RGB(A) and dest has the same format as source.
bool boxFilter(IDirect3DSurface9 *source, IDirect3DSurface9 *dest);
private:
Context *mContext;
......@@ -44,8 +48,9 @@ class Blit
bool setFormatConvertShaders(GLenum destFormat);
IDirect3DTexture9 *copySurfaceToTexture(IDirect3DSurface9 *surface, const RECT &sourceRect);
void setViewport(const RECT &sourceRect, GLint xoffset, GLint yoffset, IDirect3DSurface9 *dest);
void setViewport(const RECT &sourceRect, GLint xoffset, GLint yoffset);
void setCommonBlitState();
RECT getSurfaceRect(IDirect3DSurface9 *surface) const;
// This enum is used to index mCompiledShaders and mShaderSource.
enum ShaderId
......
......@@ -549,9 +549,9 @@ void Texture2D::copyImage(GLint level, GLenum internalFormat, GLint x, GLint y,
{
RECT sourceRect;
sourceRect.left = x;
sourceRect.top = y + height;
sourceRect.right = x + width;
sourceRect.bottom = y;
sourceRect.top = source->getHeight() - (y + height);
sourceRect.bottom = source->getHeight() - y;
IDirect3DSurface9 *dest;
HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
......@@ -584,9 +584,9 @@ void Texture2D::copySubImage(GLint level, GLint xoffset, GLint yoffset, GLint x,
RECT sourceRect;
sourceRect.left = x;
sourceRect.top = y + height;
sourceRect.right = x + width;
sourceRect.bottom = y;
sourceRect.top = source->getHeight() - (y + height);
sourceRect.bottom = source->getHeight() - y;
IDirect3DSurface9 *dest;
HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
......@@ -796,6 +796,50 @@ bool Texture2D::dirtyImageData() const
return false;
}
void Texture2D::generateMipmaps()
{
if (!isPow2(mImageArray[0].width) || !isPow2(mImageArray[0].height))
{
return error(GL_INVALID_OPERATION);
}
// Purge array levels 1 through q and reset them to represent the generated mipmap levels.
unsigned int q = log2(std::max(mWidth, mHeight));
for (unsigned int i = 1; i <= q; i++)
{
if (mImageArray[i].surface != NULL)
{
mImageArray[i].surface->Release();
mImageArray[i].surface = NULL;
}
mImageArray[i].dirty = false;
mImageArray[i].format = mImageArray[0].format;
mImageArray[i].width = std::max(mImageArray[0].width >> i, 1);
mImageArray[i].height = std::max(mImageArray[0].height >> i, 1);
}
getRenderTarget();
for (unsigned int i = 1; i <= q; i++)
{
IDirect3DSurface9 *upper = NULL;
IDirect3DSurface9 *lower = NULL;
mTexture->GetSurfaceLevel(i-1, &upper);
mTexture->GetSurfaceLevel(i, &lower);
if (upper != NULL && lower != NULL)
{
getBlitter()->boxFilter(upper, lower);
}
if (upper != NULL) upper->Release();
if (lower != NULL) lower->Release();
}
}
TextureCubeMap::TextureCubeMap(Context *context) : Texture(context)
{
mTexture = NULL;
......@@ -1184,9 +1228,9 @@ void TextureCubeMap::copyImage(GLenum face, GLint level, GLenum internalFormat,
{
RECT sourceRect;
sourceRect.left = x;
sourceRect.top = y + height;
sourceRect.right = x + width;
sourceRect.bottom = y;
sourceRect.top = source->getHeight() - (y + height);
sourceRect.bottom = source->getHeight() - y;
IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
......@@ -1251,9 +1295,9 @@ void TextureCubeMap::copySubImage(GLenum face, GLint level, GLint xoffset, GLint
RECT sourceRect;
sourceRect.left = x;
sourceRect.top = y + height;
sourceRect.right = x + width;
sourceRect.bottom = y;
sourceRect.top = source->getHeight() - (y + height);
sourceRect.bottom = source->getHeight() - y;
IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
......@@ -1261,4 +1305,70 @@ void TextureCubeMap::copySubImage(GLenum face, GLint level, GLint xoffset, GLint
dest->Release();
}
bool TextureCubeMap::isCubeComplete() const
{
if (mImageArray[0][0].width == 0)
{
return false;
}
for (unsigned int f = 1; f < 6; f++)
{
if (mImageArray[f][0].width != mImageArray[0][0].width
|| mImageArray[f][0].format != mImageArray[0][0].format)
{
return false;
}
}
return true;
}
void TextureCubeMap::generateMipmaps()
{
if (!isPow2(mImageArray[0][0].width) || !isCubeComplete())
{
return error(GL_INVALID_OPERATION);
}
// Purge array levels 1 through q and reset them to represent the generated mipmap levels.
unsigned int q = log2(mImageArray[0][0].width);
for (unsigned int f = 0; f < 6; f++)
{
for (unsigned int i = 1; i <= q; i++)
{
if (mImageArray[f][i].surface != NULL)
{
mImageArray[f][i].surface->Release();
mImageArray[f][i].surface = NULL;
}
mImageArray[f][i].dirty = false;
mImageArray[f][i].format = mImageArray[f][0].format;
mImageArray[f][i].width = std::max(mImageArray[f][0].width >> i, 1);
mImageArray[f][i].height = mImageArray[f][i].width;
}
}
getRenderTarget();
for (unsigned int f = 0; f < 6; f++)
{
for (unsigned int i = 1; i <= q; i++)
{
IDirect3DSurface9 *upper = getCubeMapSurface(f, i-1);
IDirect3DSurface9 *lower = getCubeMapSurface(f, i);
if (upper != NULL && lower != NULL)
{
getBlitter()->boxFilter(upper, lower);
}
if (upper != NULL) upper->Release();
if (lower != NULL) lower->Release();
}
}
}
}
......@@ -57,6 +57,8 @@ class Texture : public Colorbuffer
IDirect3DSurface9 *getRenderTarget(GLenum target);
IDirect3DSurface9 *getRenderTarget() { return getRenderTarget(GL_TEXTURE_2D); } // FIXME: to be removed once FBO rendering is completed.
virtual void generateMipmaps() = 0;
protected:
// Helper structure representing a single image layer
struct Image
......@@ -128,6 +130,8 @@ class Texture2D : public Texture
bool isComplete() const;
virtual void generateMipmaps();
private:
DISALLOW_COPY_AND_ASSIGN(Texture2D);
......@@ -169,6 +173,8 @@ class TextureCubeMap : public Texture
bool isComplete() const;
virtual void generateMipmaps();
private:
DISALLOW_COPY_AND_ASSIGN(TextureCubeMap);
......@@ -185,6 +191,8 @@ class TextureCubeMap : public Texture
static unsigned int faceIndex(GLenum face);
bool isCubeComplete() const;
void setImage(int face, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels);
void commitRect(GLenum faceTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height);
bool redefineTexture(GLint level, GLenum internalFormat, GLsizei width);
......
......@@ -1751,7 +1751,28 @@ void __stdcall glGenerateMipmap(GLenum target)
try
{
UNIMPLEMENTED(); // FIXME
gl::Context *context = gl::getContext();
if (context)
{
gl::Texture *texture;
switch (target)
{
case GL_TEXTURE_2D:
texture = context->getTexture2D();
break;
case GL_TEXTURE_CUBE_MAP:
texture = context->getTextureCubeMap();
break;
default:
return error(GL_INVALID_ENUM);
}
texture->generateMipmaps();
}
}
catch(std::bad_alloc&)
{
......
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