Commit 62815bf4 by Austin Kinross Committed by Geoff Lang

Improve D3D11 FL9_3 zero-LOD workaround (e.g. TextureCubes)

D3D11 Feature Level 9_3 can't disable mipmaps on a mipmapped texture, and sample from level zero of it. A previous commit added a workaround for this in ANGLE to Texture2Ds. This commit fixes some minor issues in that commit, and extends the workaround to apply to TextureCubes too. Change-Id: Ic97321af6f8bbf7ad5d96e58655c342db3978a6a Reviewed-on: https://chromium-review.googlesource.com/241944Tested-by: 's avatarAustin Kinross <aukinros@microsoft.com> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 398c5a2a
...@@ -135,7 +135,7 @@ class RendererD3D : public Renderer ...@@ -135,7 +135,7 @@ class RendererD3D : public Renderer
virtual gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) = 0; virtual gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) = 0;
virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain) = 0; virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain) = 0;
virtual TextureStorage *createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly) = 0; virtual TextureStorage *createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly) = 0;
virtual TextureStorage *createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels) = 0; virtual TextureStorage *createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly) = 0;
virtual TextureStorage *createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels) = 0; virtual TextureStorage *createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels) = 0;
virtual TextureStorage *createTextureStorage2DArray(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels) = 0; virtual TextureStorage *createTextureStorage2DArray(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels) = 0;
......
...@@ -352,9 +352,6 @@ gl::Error TextureD3D::generateMipmaps() ...@@ -352,9 +352,6 @@ gl::Error TextureD3D::generateMipmaps()
return gl::Error(GL_NO_ERROR); // no-op return gl::Error(GL_NO_ERROR); // no-op
} }
// Set up proper mipmap chain in our Image array.
initMipmapsImages();
if (mTexStorage && mRenderer->getWorkarounds().zeroMaxLodWorkaround) if (mTexStorage && mRenderer->getWorkarounds().zeroMaxLodWorkaround)
{ {
// Switch to using the mipmapped texture. // Switch to using the mipmapped texture.
...@@ -365,6 +362,9 @@ gl::Error TextureD3D::generateMipmaps() ...@@ -365,6 +362,9 @@ gl::Error TextureD3D::generateMipmaps()
} }
} }
// Set up proper mipmap chain in our Image array.
initMipmapsImages();
// We know that all layers have the same dimension, for the texture to be complete // We know that all layers have the same dimension, for the texture to be complete
GLint layerCount = static_cast<GLint>(getLayerCount(0)); GLint layerCount = static_cast<GLint>(getLayerCount(0));
...@@ -728,7 +728,9 @@ gl::Error TextureD3D_2D::copyImage(GLenum target, size_t level, const gl::Rectan ...@@ -728,7 +728,9 @@ gl::Error TextureD3D_2D::copyImage(GLenum target, size_t level, const gl::Rectan
gl::ImageIndex index = gl::ImageIndex::Make2D(level); gl::ImageIndex index = gl::ImageIndex::Make2D(level);
gl::Offset destOffset(0, 0, 0); gl::Offset destOffset(0, 0, 0);
if (!canCreateRenderTargetForImage(index)) // If the zero max LOD workaround is active, then we can't sample from individual layers of the framebuffer in shaders,
// so we should use the non-rendering copy path.
if (!canCreateRenderTargetForImage(index) || mRenderer->getWorkarounds().zeroMaxLodWorkaround)
{ {
gl::Error error = mImageArray[level]->copy(destOffset, sourceArea, source); gl::Error error = mImageArray[level]->copy(destOffset, sourceArea, source);
if (error.isError()) if (error.isError())
...@@ -771,7 +773,9 @@ gl::Error TextureD3D_2D::copySubImage(GLenum target, size_t level, const gl::Off ...@@ -771,7 +773,9 @@ gl::Error TextureD3D_2D::copySubImage(GLenum target, size_t level, const gl::Off
gl::ImageIndex index = gl::ImageIndex::Make2D(level); gl::ImageIndex index = gl::ImageIndex::Make2D(level);
if (!canCreateRenderTargetForImage(index)) // If the zero max LOD workaround is active, then we can't sample from individual layers of the framebuffer in shaders,
// so we should use the non-rendering copy path.
if (!canCreateRenderTargetForImage(index) || mRenderer->getWorkarounds().zeroMaxLodWorkaround)
{ {
gl::Error error = mImageArray[level]->copy(destOffset, sourceArea, source); gl::Error error = mImageArray[level]->copy(destOffset, sourceArea, source);
if (error.isError()) if (error.isError())
...@@ -1030,11 +1034,9 @@ gl::Error TextureD3D_2D::createCompleteStorage(bool renderTarget, TextureStorage ...@@ -1030,11 +1034,9 @@ gl::Error TextureD3D_2D::createCompleteStorage(bool renderTarget, TextureStorage
// If any of the CPU images (levels >= 1) are dirty, then the textureStorage2D should use the mipped texture to begin with. // If any of the CPU images (levels >= 1) are dirty, then the textureStorage2D should use the mipped texture to begin with.
// Otherwise, it should use the level-zero-only texture. // Otherwise, it should use the level-zero-only texture.
hintLevelZeroOnly = true; hintLevelZeroOnly = true;
int level = 1; for (int level = 1; level < levels && hintLevelZeroOnly; level++)
while (hintLevelZeroOnly && level < levels)
{ {
hintLevelZeroOnly = !(mImageArray[level]->isDirty() && isLevelComplete(level)); hintLevelZeroOnly = !(mImageArray[level]->isDirty() && isLevelComplete(level));
level += 1;
} }
} }
...@@ -1381,7 +1383,8 @@ gl::Error TextureD3D_Cube::setStorage(GLenum target, size_t levels, GLenum inter ...@@ -1381,7 +1383,8 @@ gl::Error TextureD3D_Cube::setStorage(GLenum target, size_t levels, GLenum inter
// TODO(geofflang): Verify storage creation had no errors // TODO(geofflang): Verify storage creation had no errors
bool renderTarget = IsRenderTargetUsage(mUsage); bool renderTarget = IsRenderTargetUsage(mUsage);
TextureStorage *storage = mRenderer->createTextureStorageCube(internalFormat, renderTarget, size.width, levels);
TextureStorage *storage = mRenderer->createTextureStorageCube(internalFormat, renderTarget, size.width, levels, false);
gl::Error error = setCompleteTexStorage(storage); gl::Error error = setCompleteTexStorage(storage);
if (error.isError()) if (error.isError())
...@@ -1524,8 +1527,23 @@ gl::Error TextureD3D_Cube::createCompleteStorage(bool renderTarget, TextureStora ...@@ -1524,8 +1527,23 @@ gl::Error TextureD3D_Cube::createCompleteStorage(bool renderTarget, TextureStora
// use existing storage level count, when previously specified by TexStorage*D // use existing storage level count, when previously specified by TexStorage*D
GLint levels = (mTexStorage ? mTexStorage->getLevelCount() : creationLevels(size, size, 1)); GLint levels = (mTexStorage ? mTexStorage->getLevelCount() : creationLevels(size, size, 1));
bool hintLevelZeroOnly = false;
if (mRenderer->getWorkarounds().zeroMaxLodWorkaround)
{
// If any of the CPU images (levels >= 1) are dirty, then the textureStorage should use the mipped texture to begin with.
// Otherwise, it should use the level-zero-only texture.
hintLevelZeroOnly = true;
for (int faceIndex = 0; faceIndex < 6 && hintLevelZeroOnly; faceIndex++)
{
for (int level = 1; level < levels && hintLevelZeroOnly; level++)
{
hintLevelZeroOnly = !(mImageArray[faceIndex][level]->isDirty() && isFaceLevelComplete(faceIndex, level));
}
}
}
// TODO (geofflang): detect if storage creation succeeded // TODO (geofflang): detect if storage creation succeeded
*outTexStorage = mRenderer->createTextureStorageCube(getBaseLevelInternalFormat(), renderTarget, size, levels); *outTexStorage = mRenderer->createTextureStorageCube(getBaseLevelInternalFormat(), renderTarget, size, levels, hintLevelZeroOnly);
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
......
...@@ -2829,9 +2829,9 @@ TextureStorage *Renderer11::createTextureStorage2D(GLenum internalformat, bool r ...@@ -2829,9 +2829,9 @@ TextureStorage *Renderer11::createTextureStorage2D(GLenum internalformat, bool r
return new TextureStorage11_2D(this, internalformat, renderTarget, width, height, levels, hintLevelZeroOnly); return new TextureStorage11_2D(this, internalformat, renderTarget, width, height, levels, hintLevelZeroOnly);
} }
TextureStorage *Renderer11::createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels) TextureStorage *Renderer11::createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly)
{ {
return new TextureStorage11_Cube(this, internalformat, renderTarget, size, levels); return new TextureStorage11_Cube(this, internalformat, renderTarget, size, levels, hintLevelZeroOnly);
} }
TextureStorage *Renderer11::createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels) TextureStorage *Renderer11::createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels)
......
...@@ -155,7 +155,7 @@ class Renderer11 : public RendererD3D ...@@ -155,7 +155,7 @@ class Renderer11 : public RendererD3D
gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) override; gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) override;
virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain); virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain);
virtual TextureStorage *createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly); virtual TextureStorage *createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly);
virtual TextureStorage *createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels); virtual TextureStorage *createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly);
virtual TextureStorage *createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels); virtual TextureStorage *createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels);
virtual TextureStorage *createTextureStorage2DArray(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels); virtual TextureStorage *createTextureStorage2DArray(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels);
......
...@@ -426,8 +426,28 @@ gl::Error TextureStorage11::copySubresourceLevel(ID3D11Resource* dstTexture, uns ...@@ -426,8 +426,28 @@ gl::Error TextureStorage11::copySubresourceLevel(ID3D11Resource* dstTexture, uns
unsigned int srcSubresource = getSubresourceIndex(index); unsigned int srcSubresource = getSubresourceIndex(index);
ID3D11DeviceContext *context = mRenderer->getDeviceContext(); ID3D11DeviceContext *context = mRenderer->getDeviceContext();
// D3D11 can't perform partial CopySubresourceRegion on depth/stencil textures, so pSrcBox should be NULL.
D3D11_BOX srcBox;
D3D11_BOX *pSrcBox = NULL;
if (mRenderer->getFeatureLevel() <= D3D_FEATURE_LEVEL_9_3)
{
// However, D3D10Level9 doesn't always perform CopySubresourceRegion correctly unless the source box
// is specified. This is okay, since we don't perform CopySubresourceRegion on depth/stencil
// textures on 9_3.
ASSERT(d3d11::GetDXGIFormatInfo(mTextureFormat).depthBits == 0);
ASSERT(d3d11::GetDXGIFormatInfo(mTextureFormat).stencilBits == 0);
srcBox.left = region.x;
srcBox.right = region.x + region.width;
srcBox.top = region.y;
srcBox.bottom = region.y + region.height;
srcBox.front = region.z;
srcBox.back = region.z + region.depth;
pSrcBox = &srcBox;
}
context->CopySubresourceRegion(dstTexture, dstSubresource, region.x, region.y, region.z, context->CopySubresourceRegion(dstTexture, dstSubresource, region.x, region.y, region.z,
srcTexture, srcSubresource, NULL); srcTexture, srcSubresource, pSrcBox);
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
...@@ -662,7 +682,7 @@ TextureStorage11_2D::TextureStorage11_2D(Renderer11 *renderer, GLenum internalfo ...@@ -662,7 +682,7 @@ TextureStorage11_2D::TextureStorage11_2D(Renderer11 *renderer, GLenum internalfo
mTextureHeight = height; mTextureHeight = height;
mTextureDepth = 1; mTextureDepth = 1;
if (hintLevelZeroOnly) if (hintLevelZeroOnly && levels > 1)
{ {
//The LevelZeroOnly hint should only be true if the zero max LOD workaround is active. //The LevelZeroOnly hint should only be true if the zero max LOD workaround is active.
ASSERT(mRenderer->getWorkarounds().zeroMaxLodWorkaround); ASSERT(mRenderer->getWorkarounds().zeroMaxLodWorkaround);
...@@ -787,7 +807,7 @@ gl::Error TextureStorage11_2D::copyToStorage(TextureStorage *destStorage) ...@@ -787,7 +807,7 @@ gl::Error TextureStorage11_2D::copyToStorage(TextureStorage *destStorage)
gl::Error TextureStorage11_2D::useLevelZeroWorkaroundTexture(bool useLevelZeroTexture) gl::Error TextureStorage11_2D::useLevelZeroWorkaroundTexture(bool useLevelZeroTexture)
{ {
if (useLevelZeroTexture) if (useLevelZeroTexture && mMipLevels > 1)
{ {
if (!mUseLevelZeroTexture && mTexture) if (!mUseLevelZeroTexture && mTexture)
{ {
...@@ -798,8 +818,8 @@ gl::Error TextureStorage11_2D::useLevelZeroWorkaroundTexture(bool useLevelZeroTe ...@@ -798,8 +818,8 @@ gl::Error TextureStorage11_2D::useLevelZeroWorkaroundTexture(bool useLevelZeroTe
} }
// Pull data back from the mipped texture if necessary. // Pull data back from the mipped texture if necessary.
ASSERT(mTexture); ASSERT(mLevelZeroTexture);
ID3D11DeviceContext* context = mRenderer->getDeviceContext(); ID3D11DeviceContext *context = mRenderer->getDeviceContext();
context->CopySubresourceRegion(mLevelZeroTexture, 0, 0, 0, 0, mTexture, 0, NULL); context->CopySubresourceRegion(mLevelZeroTexture, 0, 0, 0, 0, mTexture, 0, NULL);
} }
...@@ -817,7 +837,7 @@ gl::Error TextureStorage11_2D::useLevelZeroWorkaroundTexture(bool useLevelZeroTe ...@@ -817,7 +837,7 @@ gl::Error TextureStorage11_2D::useLevelZeroWorkaroundTexture(bool useLevelZeroTe
// Pull data back from the level zero texture if necessary. // Pull data back from the level zero texture if necessary.
ASSERT(mTexture); ASSERT(mTexture);
ID3D11DeviceContext* context = mRenderer->getDeviceContext(); ID3D11DeviceContext *context = mRenderer->getDeviceContext();
context->CopySubresourceRegion(mTexture, 0, 0, 0, 0, mLevelZeroTexture, 0, NULL); context->CopySubresourceRegion(mTexture, 0, 0, 0, 0, mLevelZeroTexture, 0, NULL);
} }
...@@ -906,7 +926,7 @@ gl::Error TextureStorage11_2D::releaseAssociatedImage(const gl::ImageIndex &inde ...@@ -906,7 +926,7 @@ gl::Error TextureStorage11_2D::releaseAssociatedImage(const gl::ImageIndex &inde
gl::Error TextureStorage11_2D::getResource(ID3D11Resource **outResource) gl::Error TextureStorage11_2D::getResource(ID3D11Resource **outResource)
{ {
if (mUseLevelZeroTexture) if (mUseLevelZeroTexture && mMipLevels > 1)
{ {
gl::Error error = ensureTextureExists(1); gl::Error error = ensureTextureExists(1);
if (error.isError()) if (error.isError())
...@@ -947,7 +967,9 @@ gl::Error TextureStorage11_2D::getMippedResource(ID3D11Resource **outResource) ...@@ -947,7 +967,9 @@ gl::Error TextureStorage11_2D::getMippedResource(ID3D11Resource **outResource)
gl::Error TextureStorage11_2D::ensureTextureExists(int mipLevels) gl::Error TextureStorage11_2D::ensureTextureExists(int mipLevels)
{ {
ID3D11Texture2D** outputTexture = (mipLevels == 1 && mRenderer->getWorkarounds().zeroMaxLodWorkaround) ? &mLevelZeroTexture : &mTexture; // If mMipLevels = 1 then always use mTexture rather than mLevelZeroTexture.
bool useLevelZeroTexture = mRenderer->getWorkarounds().zeroMaxLodWorkaround ? (mipLevels == 1) && (mMipLevels > 1) : false;
ID3D11Texture2D **outputTexture = useLevelZeroTexture ? &mLevelZeroTexture : &mTexture;
// if the width or height is not positive this should be treated as an incomplete texture // if the width or height is not positive this should be treated as an incomplete texture
// we handle that here by skipping the d3d texture creation // we handle that here by skipping the d3d texture creation
...@@ -1126,7 +1148,7 @@ gl::Error TextureStorage11_2D::createSRV(int baseLevel, int mipLevels, DXGI_FORM ...@@ -1126,7 +1148,7 @@ gl::Error TextureStorage11_2D::createSRV(int baseLevel, int mipLevels, DXGI_FORM
ASSERT(baseLevel == 0); ASSERT(baseLevel == 0);
// This code also assumes that the incoming texture equals either mLevelZeroTexture or mTexture. // This code also assumes that the incoming texture equals either mLevelZeroTexture or mTexture.
if (mipLevels == 1) if (mipLevels == 1 && mMipLevels > 1)
{ {
// We must use a SRV on the level-zero-only texture. // We must use a SRV on the level-zero-only texture.
ASSERT(mLevelZeroTexture != NULL && texture == mLevelZeroTexture); ASSERT(mLevelZeroTexture != NULL && texture == mLevelZeroTexture);
...@@ -1220,7 +1242,7 @@ gl::Error TextureStorage11_2D::getSwizzleRenderTarget(int mipLevel, ID3D11Render ...@@ -1220,7 +1242,7 @@ gl::Error TextureStorage11_2D::getSwizzleRenderTarget(int mipLevel, ID3D11Render
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
TextureStorage11_Cube::TextureStorage11_Cube(Renderer11 *renderer, GLenum internalformat, bool renderTarget, int size, int levels) TextureStorage11_Cube::TextureStorage11_Cube(Renderer11 *renderer, GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly)
: TextureStorage11(renderer, GetTextureBindFlags(internalformat, renderer->getFeatureLevel(), renderTarget)) : TextureStorage11(renderer, GetTextureBindFlags(internalformat, renderer->getFeatureLevel(), renderTarget))
{ {
mTexture = NULL; mTexture = NULL;
...@@ -1236,6 +1258,14 @@ TextureStorage11_Cube::TextureStorage11_Cube(Renderer11 *renderer, GLenum intern ...@@ -1236,6 +1258,14 @@ TextureStorage11_Cube::TextureStorage11_Cube(Renderer11 *renderer, GLenum intern
} }
} }
mLevelZeroTexture = NULL;
mUseLevelZeroTexture = false;
for (unsigned int face = 0; face < CUBE_FACE_COUNT; face++)
{
mLevelZeroRenderTarget[face] = NULL;
}
mInternalFormat = internalformat; mInternalFormat = internalformat;
const d3d11::TextureFormat &formatInfo = d3d11::GetTextureFormatInfo(internalformat, renderer->getFeatureLevel()); const d3d11::TextureFormat &formatInfo = d3d11::GetTextureFormatInfo(internalformat, renderer->getFeatureLevel());
...@@ -1256,6 +1286,13 @@ TextureStorage11_Cube::TextureStorage11_Cube(Renderer11 *renderer, GLenum intern ...@@ -1256,6 +1286,13 @@ TextureStorage11_Cube::TextureStorage11_Cube(Renderer11 *renderer, GLenum intern
mTextureHeight = size; mTextureHeight = size;
mTextureDepth = 1; mTextureDepth = 1;
if (hintLevelZeroOnly && levels > 1)
{
//The LevelZeroOnly hint should only be true if the zero max LOD workaround is active.
ASSERT(mRenderer->getWorkarounds().zeroMaxLodWorkaround);
mUseLevelZeroTexture = true;
}
initializeSerials(getLevelCount() * CUBE_FACE_COUNT, CUBE_FACE_COUNT); initializeSerials(getLevelCount() * CUBE_FACE_COUNT, CUBE_FACE_COUNT);
} }
...@@ -1281,6 +1318,12 @@ TextureStorage11_Cube::~TextureStorage11_Cube() ...@@ -1281,6 +1318,12 @@ TextureStorage11_Cube::~TextureStorage11_Cube()
SafeRelease(mTexture); SafeRelease(mTexture);
SafeRelease(mSwizzleTexture); SafeRelease(mSwizzleTexture);
SafeRelease(mLevelZeroTexture);
for (unsigned int face = 0; face < CUBE_FACE_COUNT; face++)
{
SafeDelete(mLevelZeroRenderTarget[face]);
}
for (unsigned int level = 0; level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) for (unsigned int level = 0; level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
{ {
...@@ -1298,6 +1341,147 @@ TextureStorage11_Cube *TextureStorage11_Cube::makeTextureStorage11_Cube(TextureS ...@@ -1298,6 +1341,147 @@ TextureStorage11_Cube *TextureStorage11_Cube::makeTextureStorage11_Cube(TextureS
return static_cast<TextureStorage11_Cube*>(storage); return static_cast<TextureStorage11_Cube*>(storage);
} }
UINT TextureStorage11_Cube::getSubresourceIndex(const gl::ImageIndex &index) const
{
if (mRenderer->getWorkarounds().zeroMaxLodWorkaround && mUseLevelZeroTexture && index.mipIndex == 0)
{
UINT arraySlice = static_cast<UINT>(index.hasLayer() ? index.layerIndex : 0);
UINT subresource = D3D11CalcSubresource(0, arraySlice, 1);
ASSERT(subresource != std::numeric_limits<UINT>::max());
return subresource;
}
else
{
UINT mipSlice = static_cast<UINT>(index.mipIndex + mTopLevel);
UINT arraySlice = static_cast<UINT>(index.hasLayer() ? index.layerIndex : 0);
UINT subresource = D3D11CalcSubresource(mipSlice, arraySlice, mMipLevels);
ASSERT(subresource != std::numeric_limits<UINT>::max());
return subresource;
}
}
gl::Error TextureStorage11_Cube::copyToStorage(TextureStorage *destStorage)
{
ASSERT(destStorage);
TextureStorage11_Cube *dest11 = TextureStorage11_Cube::makeTextureStorage11_Cube(destStorage);
if (mRenderer->getWorkarounds().zeroMaxLodWorkaround)
{
ID3D11DeviceContext *immediateContext = mRenderer->getDeviceContext();
// If either mTexture or mLevelZeroTexture exist, then we need to copy them into the corresponding textures in destStorage.
if (mTexture)
{
gl::Error error = dest11->useLevelZeroWorkaroundTexture(false);
if (error.isError())
{
return error;
}
ID3D11Resource *destResource = NULL;
error = dest11->getResource(&destResource);
if (error.isError())
{
return error;
}
immediateContext->CopyResource(destResource, mTexture);
}
if (mLevelZeroTexture)
{
gl::Error error = dest11->useLevelZeroWorkaroundTexture(true);
if (error.isError())
{
return error;
}
ID3D11Resource *destResource = NULL;
error = dest11->getResource(&destResource);
if (error.isError())
{
return error;
}
immediateContext->CopyResource(destResource, mLevelZeroTexture);
}
}
else
{
ID3D11Resource *sourceResouce = NULL;
gl::Error error = getResource(&sourceResouce);
if (error.isError())
{
return error;
}
ID3D11Resource *destResource = NULL;
error = dest11->getResource(&destResource);
if (error.isError())
{
return error;
}
ID3D11DeviceContext *immediateContext = mRenderer->getDeviceContext();
immediateContext->CopyResource(destResource, sourceResouce);
}
dest11->invalidateSwizzleCache();
return gl::Error(GL_NO_ERROR);
}
gl::Error TextureStorage11_Cube::useLevelZeroWorkaroundTexture(bool useLevelZeroTexture)
{
if (useLevelZeroTexture && mMipLevels > 1)
{
if (!mUseLevelZeroTexture && mTexture)
{
gl::Error error = ensureTextureExists(1);
if (error.isError())
{
return error;
}
// Pull data back from the mipped texture if necessary.
ASSERT(mLevelZeroTexture);
ID3D11DeviceContext *context = mRenderer->getDeviceContext();
for (int face = 0; face < 6; face++)
{
context->CopySubresourceRegion(mLevelZeroTexture, D3D11CalcSubresource(0, face, 1), 0, 0, 0, mTexture, face * mMipLevels, NULL);
}
}
mUseLevelZeroTexture = true;
}
else
{
if (mUseLevelZeroTexture && mLevelZeroTexture)
{
gl::Error error = ensureTextureExists(mMipLevels);
if (error.isError())
{
return error;
}
// Pull data back from the level zero texture if necessary.
ASSERT(mTexture);
ID3D11DeviceContext *context = mRenderer->getDeviceContext();
for (int face = 0; face < 6; face++)
{
context->CopySubresourceRegion(mTexture, D3D11CalcSubresource(0, face, mMipLevels), 0, 0, 0, mLevelZeroTexture, face, NULL);
}
}
mUseLevelZeroTexture = false;
}
return gl::Error(GL_NO_ERROR);
}
void TextureStorage11_Cube::associateImage(Image11* image, const gl::ImageIndex &index) void TextureStorage11_Cube::associateImage(Image11* image, const gl::ImageIndex &index)
{ {
GLint level = index.mipIndex; GLint level = index.mipIndex;
...@@ -1396,9 +1580,54 @@ gl::Error TextureStorage11_Cube::releaseAssociatedImage(const gl::ImageIndex &in ...@@ -1396,9 +1580,54 @@ gl::Error TextureStorage11_Cube::releaseAssociatedImage(const gl::ImageIndex &in
gl::Error TextureStorage11_Cube::getResource(ID3D11Resource **outResource) gl::Error TextureStorage11_Cube::getResource(ID3D11Resource **outResource)
{ {
if (mUseLevelZeroTexture && mMipLevels > 1)
{
gl::Error error = ensureTextureExists(1);
if (error.isError())
{
return error;
}
*outResource = mLevelZeroTexture;
return gl::Error(GL_NO_ERROR);
}
else
{
gl::Error error = ensureTextureExists(mMipLevels);
if (error.isError())
{
return error;
}
*outResource = mTexture;
return gl::Error(GL_NO_ERROR);
}
}
gl::Error TextureStorage11_Cube::getMippedResource(ID3D11Resource **outResource)
{
// This shouldn't be called unless the zero max LOD workaround is active.
ASSERT(mRenderer->getWorkarounds().zeroMaxLodWorkaround);
gl::Error error = ensureTextureExists(mMipLevels);
if (error.isError())
{
return error;
}
*outResource = mTexture;
return gl::Error(GL_NO_ERROR);
}
gl::Error TextureStorage11_Cube::ensureTextureExists(int mipLevels)
{
// If mMipLevels = 1 then always use mTexture rather than mLevelZeroTexture.
bool useLevelZeroTexture = mRenderer->getWorkarounds().zeroMaxLodWorkaround ? (mipLevels == 1) && (mMipLevels > 1) : false;
ID3D11Texture2D **outputTexture = useLevelZeroTexture ? &mLevelZeroTexture : &mTexture;
// if the size is not positive this should be treated as an incomplete texture // if the size is not positive this should be treated as an incomplete texture
// we handle that here by skipping the d3d texture creation // we handle that here by skipping the d3d texture creation
if (mTexture == NULL && mTextureWidth > 0 && mTextureHeight > 0) if (*outputTexture == NULL && mTextureWidth > 0 && mTextureHeight > 0)
{ {
ASSERT(mMipLevels > 0); ASSERT(mMipLevels > 0);
...@@ -1407,7 +1636,7 @@ gl::Error TextureStorage11_Cube::getResource(ID3D11Resource **outResource) ...@@ -1407,7 +1636,7 @@ gl::Error TextureStorage11_Cube::getResource(ID3D11Resource **outResource)
D3D11_TEXTURE2D_DESC desc; D3D11_TEXTURE2D_DESC desc;
desc.Width = mTextureWidth; desc.Width = mTextureWidth;
desc.Height = mTextureHeight; desc.Height = mTextureHeight;
desc.MipLevels = mMipLevels; desc.MipLevels = mipLevels;
desc.ArraySize = CUBE_FACE_COUNT; desc.ArraySize = CUBE_FACE_COUNT;
desc.Format = mTextureFormat; desc.Format = mTextureFormat;
desc.SampleDesc.Count = 1; desc.SampleDesc.Count = 1;
...@@ -1417,7 +1646,7 @@ gl::Error TextureStorage11_Cube::getResource(ID3D11Resource **outResource) ...@@ -1417,7 +1646,7 @@ gl::Error TextureStorage11_Cube::getResource(ID3D11Resource **outResource)
desc.CPUAccessFlags = 0; desc.CPUAccessFlags = 0;
desc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE; desc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;
HRESULT result = device->CreateTexture2D(&desc, NULL, &mTexture); HRESULT result = device->CreateTexture2D(&desc, NULL, outputTexture);
// this can happen from windows TDR // this can happen from windows TDR
if (d3d11::isDeviceLostError(result)) if (d3d11::isDeviceLostError(result))
...@@ -1432,7 +1661,6 @@ gl::Error TextureStorage11_Cube::getResource(ID3D11Resource **outResource) ...@@ -1432,7 +1661,6 @@ gl::Error TextureStorage11_Cube::getResource(ID3D11Resource **outResource)
} }
} }
*outResource = mTexture;
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
...@@ -1456,14 +1684,53 @@ gl::Error TextureStorage11_Cube::getRenderTarget(const gl::ImageIndex &index, Re ...@@ -1456,14 +1684,53 @@ gl::Error TextureStorage11_Cube::getRenderTarget(const gl::ImageIndex &index, Re
return error; return error;
} }
if (mUseLevelZeroTexture)
{
if (!mLevelZeroRenderTarget[faceIndex])
{
D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
rtvDesc.Format = mRenderTargetFormat;
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
rtvDesc.Texture2DArray.MipSlice = mTopLevel + level;
rtvDesc.Texture2DArray.FirstArraySlice = faceIndex;
rtvDesc.Texture2DArray.ArraySize = 1;
ID3D11RenderTargetView *rtv;
result = device->CreateRenderTargetView(mLevelZeroTexture, &rtvDesc, &rtv);
if (result == E_OUTOFMEMORY)
{
return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal render target view for texture storage, result: 0x%X.", result);
}
ASSERT(SUCCEEDED(result));
mLevelZeroRenderTarget[faceIndex] = new TextureRenderTarget11(rtv, mLevelZeroTexture, NULL, mInternalFormat, getLevelWidth(level), getLevelHeight(level), 1, 0);
// RenderTarget will take ownership of these resources
SafeRelease(rtv);
}
ASSERT(outRT);
*outRT = mLevelZeroRenderTarget[faceIndex];
return gl::Error(GL_NO_ERROR);
}
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
srvDesc.Format = mShaderResourceFormat; srvDesc.Format = mShaderResourceFormat;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; // Will be used with Texture2D sampler, not TextureCube
srvDesc.Texture2DArray.MostDetailedMip = mTopLevel + level; srvDesc.Texture2DArray.MostDetailedMip = mTopLevel + level;
srvDesc.Texture2DArray.MipLevels = 1; srvDesc.Texture2DArray.MipLevels = 1;
srvDesc.Texture2DArray.FirstArraySlice = faceIndex; srvDesc.Texture2DArray.FirstArraySlice = faceIndex;
srvDesc.Texture2DArray.ArraySize = 1; srvDesc.Texture2DArray.ArraySize = 1;
if (mRenderer->getFeatureLevel() <= D3D_FEATURE_LEVEL_9_3)
{
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
}
else
{
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; // Will be used with Texture2D sampler, not TextureCube
}
ID3D11ShaderResourceView *srv; ID3D11ShaderResourceView *srv;
result = device->CreateShaderResourceView(texture, &srvDesc, &srv); result = device->CreateShaderResourceView(texture, &srvDesc, &srv);
...@@ -1560,8 +1827,30 @@ gl::Error TextureStorage11_Cube::createSRV(int baseLevel, int mipLevels, DXGI_FO ...@@ -1560,8 +1827,30 @@ gl::Error TextureStorage11_Cube::createSRV(int baseLevel, int mipLevels, DXGI_FO
srvDesc.TextureCube.MostDetailedMip = mTopLevel + baseLevel; srvDesc.TextureCube.MostDetailedMip = mTopLevel + baseLevel;
} }
ID3D11Resource *srvTexture = texture;
if (mRenderer->getWorkarounds().zeroMaxLodWorkaround)
{
ASSERT(mTopLevel == 0);
ASSERT(baseLevel == 0);
// This code also assumes that the incoming texture equals either mLevelZeroTexture or mTexture.
if (mipLevels == 1 && mMipLevels > 1)
{
// We must use a SRV on the level-zero-only texture.
ASSERT(mLevelZeroTexture != NULL && texture == mLevelZeroTexture);
srvTexture = mLevelZeroTexture;
}
else
{
ASSERT(mipLevels == static_cast<int>(mMipLevels));
ASSERT(mTexture != NULL && texture == mTexture);
srvTexture = mTexture;
}
}
ID3D11Device *device = mRenderer->getDevice(); ID3D11Device *device = mRenderer->getDevice();
HRESULT result = device->CreateShaderResourceView(texture, &srvDesc, outSRV); HRESULT result = device->CreateShaderResourceView(srvTexture, &srvDesc, outSRV);
ASSERT(result == E_OUTOFMEMORY || SUCCEEDED(result)); ASSERT(result == E_OUTOFMEMORY || SUCCEEDED(result));
if (FAILED(result)) if (FAILED(result))
......
...@@ -50,7 +50,7 @@ class TextureStorage11 : public TextureStorage ...@@ -50,7 +50,7 @@ class TextureStorage11 : public TextureStorage
virtual bool isRenderTarget() const; virtual bool isRenderTarget() const;
virtual bool isManaged() const; virtual bool isManaged() const;
virtual int getLevelCount() const; virtual int getLevelCount() const;
UINT getSubresourceIndex(const gl::ImageIndex &index) const; virtual UINT getSubresourceIndex(const gl::ImageIndex &index) const;
gl::Error generateSwizzles(GLenum swizzleRed, GLenum swizzleGreen, GLenum swizzleBlue, GLenum swizzleAlpha); gl::Error generateSwizzles(GLenum swizzleRed, GLenum swizzleGreen, GLenum swizzleBlue, GLenum swizzleAlpha);
void invalidateSwizzleCacheLevel(int mipLevel); void invalidateSwizzleCacheLevel(int mipLevel);
...@@ -201,23 +201,32 @@ class TextureStorage11_2D : public TextureStorage11 ...@@ -201,23 +201,32 @@ class TextureStorage11_2D : public TextureStorage11
class TextureStorage11_Cube : public TextureStorage11 class TextureStorage11_Cube : public TextureStorage11
{ {
public: public:
TextureStorage11_Cube(Renderer11 *renderer, GLenum internalformat, bool renderTarget, int size, int levels); TextureStorage11_Cube(Renderer11 *renderer, GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly);
virtual ~TextureStorage11_Cube(); virtual ~TextureStorage11_Cube();
static TextureStorage11_Cube *makeTextureStorage11_Cube(TextureStorage *storage); static TextureStorage11_Cube *makeTextureStorage11_Cube(TextureStorage *storage);
virtual UINT getSubresourceIndex(const gl::ImageIndex &index) const;
virtual gl::Error getResource(ID3D11Resource **outResource); virtual gl::Error getResource(ID3D11Resource **outResource);
virtual gl::Error getMippedResource(ID3D11Resource **outResource);
virtual gl::Error getRenderTarget(const gl::ImageIndex &index, RenderTargetD3D **outRT); virtual gl::Error getRenderTarget(const gl::ImageIndex &index, RenderTargetD3D **outRT);
virtual gl::Error copyToStorage(TextureStorage *destStorage);
virtual void associateImage(Image11* image, const gl::ImageIndex &index); virtual void associateImage(Image11* image, const gl::ImageIndex &index);
virtual void disassociateImage(const gl::ImageIndex &index, Image11* expectedImage); virtual void disassociateImage(const gl::ImageIndex &index, Image11* expectedImage);
virtual bool isAssociatedImageValid(const gl::ImageIndex &index, Image11* expectedImage); virtual bool isAssociatedImageValid(const gl::ImageIndex &index, Image11* expectedImage);
virtual gl::Error releaseAssociatedImage(const gl::ImageIndex &index, Image11* incomingImage); virtual gl::Error releaseAssociatedImage(const gl::ImageIndex &index, Image11* incomingImage);
virtual gl::Error useLevelZeroWorkaroundTexture(bool useLevelZeroTexture);
protected: protected:
virtual gl::Error getSwizzleTexture(ID3D11Resource **outTexture); virtual gl::Error getSwizzleTexture(ID3D11Resource **outTexture);
virtual gl::Error getSwizzleRenderTarget(int mipLevel, ID3D11RenderTargetView **outRTV); virtual gl::Error getSwizzleRenderTarget(int mipLevel, ID3D11RenderTargetView **outRTV);
gl::Error ensureTextureExists(int mipLevels);
private: private:
DISALLOW_COPY_AND_ASSIGN(TextureStorage11_Cube); DISALLOW_COPY_AND_ASSIGN(TextureStorage11_Cube);
...@@ -229,6 +238,11 @@ class TextureStorage11_Cube : public TextureStorage11 ...@@ -229,6 +238,11 @@ class TextureStorage11_Cube : public TextureStorage11
ID3D11Texture2D *mTexture; ID3D11Texture2D *mTexture;
RenderTarget11 *mRenderTarget[CUBE_FACE_COUNT][gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS]; RenderTarget11 *mRenderTarget[CUBE_FACE_COUNT][gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS];
// Level-zero workaround members. See TextureStorage11_2D's workaround members for a description.
ID3D11Texture2D *mLevelZeroTexture;
RenderTarget11 *mLevelZeroRenderTarget[CUBE_FACE_COUNT];
bool mUseLevelZeroTexture;
ID3D11Texture2D *mSwizzleTexture; ID3D11Texture2D *mSwizzleTexture;
ID3D11RenderTargetView *mSwizzleRenderTargets[gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS]; ID3D11RenderTargetView *mSwizzleRenderTargets[gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS];
......
...@@ -2832,9 +2832,9 @@ TextureStorage *Renderer9::createTextureStorage2D(GLenum internalformat, bool re ...@@ -2832,9 +2832,9 @@ TextureStorage *Renderer9::createTextureStorage2D(GLenum internalformat, bool re
return new TextureStorage9_2D(this, internalformat, renderTarget, width, height, levels); return new TextureStorage9_2D(this, internalformat, renderTarget, width, height, levels);
} }
TextureStorage *Renderer9::createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels) TextureStorage *Renderer9::createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly)
{ {
return new TextureStorage9_Cube(this, internalformat, renderTarget, size, levels); return new TextureStorage9_Cube(this, internalformat, renderTarget, size, levels, hintLevelZeroOnly);
} }
TextureStorage *Renderer9::createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels) TextureStorage *Renderer9::createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels)
......
...@@ -164,7 +164,7 @@ class Renderer9 : public RendererD3D ...@@ -164,7 +164,7 @@ class Renderer9 : public RendererD3D
gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) override; gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) override;
virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain); virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain);
virtual TextureStorage *createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly); virtual TextureStorage *createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly);
virtual TextureStorage *createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels); virtual TextureStorage *createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly);
virtual TextureStorage *createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels); virtual TextureStorage *createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels);
virtual TextureStorage *createTextureStorage2DArray(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels); virtual TextureStorage *createTextureStorage2DArray(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels);
......
...@@ -286,7 +286,7 @@ gl::Error TextureStorage9_2D::copyToStorage(TextureStorage *destStorage) ...@@ -286,7 +286,7 @@ gl::Error TextureStorage9_2D::copyToStorage(TextureStorage *destStorage)
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
TextureStorage9_Cube::TextureStorage9_Cube(Renderer9 *renderer, GLenum internalformat, bool renderTarget, int size, int levels) TextureStorage9_Cube::TextureStorage9_Cube(Renderer9 *renderer, GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly)
: TextureStorage9(renderer, GetTextureUsage(internalformat, renderTarget)) : TextureStorage9(renderer, GetTextureUsage(internalformat, renderTarget))
{ {
mTexture = NULL; mTexture = NULL;
......
...@@ -88,7 +88,7 @@ class TextureStorage9_2D : public TextureStorage9 ...@@ -88,7 +88,7 @@ class TextureStorage9_2D : public TextureStorage9
class TextureStorage9_Cube : public TextureStorage9 class TextureStorage9_Cube : public TextureStorage9
{ {
public: public:
TextureStorage9_Cube(Renderer9 *renderer, GLenum internalformat, bool renderTarget, int size, int levels); TextureStorage9_Cube(Renderer9 *renderer, GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly);
virtual ~TextureStorage9_Cube(); virtual ~TextureStorage9_Cube();
static TextureStorage9_Cube *makeTextureStorage9_Cube(TextureStorage *storage); static TextureStorage9_Cube *makeTextureStorage9_Cube(TextureStorage *storage);
......
#include "ANGLETest.h" #include "ANGLETest.h"
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
ANGLE_TYPED_TEST_CASE(CubeMapTextureTest, ES2_D3D11); ANGLE_TYPED_TEST_CASE(CubeMapTextureTest, ES2_D3D11, ES2_D3D11_FL9_3);
template<typename T> template<typename T>
class CubeMapTextureTest : public ANGLETest class CubeMapTextureTest : public ANGLETest
......
...@@ -50,43 +50,90 @@ class MipmapTest : public ANGLETest ...@@ -50,43 +50,90 @@ class MipmapTest : public ANGLETest
} }
); );
mProgram = CompileProgram(vs, fs); m2DProgram = CompileProgram(vs, fs);
if (mProgram == 0) if (m2DProgram == 0)
{ {
FAIL() << "shader compilation failed."; FAIL() << "shader compilation failed.";
} }
mTextureUniformPosition = glGetUniformLocation(mProgram, "uTexture"); // A simple vertex shader for the texture cube
mPositionAttributePosition = glGetAttribLocation(mProgram, "aPosition"); const std::string cubeVS = SHADER_SOURCE
mTexCoordAttributePosition = glGetAttribLocation(mProgram, "aTexCoord"); (
attribute vec4 aPosition;
varying vec4 vPosition;
void main()
{
gl_Position = aPosition;
vPosition = aPosition;
}
);
// A very simple fragment shader to sample from the negative-Y face of a texture cube.
const std::string cubeFS = SHADER_SOURCE
(
precision mediump float;
uniform samplerCube uTexture;
varying vec4 vPosition;
void main()
{
gl_FragColor = textureCube(uTexture, vec3(vPosition.x, -1, vPosition.y));
}
);
mCubeProgram = CompileProgram(cubeVS, cubeFS);
if (mCubeProgram == 0)
{
FAIL() << "shader compilation failed.";
}
m2DTextureUniformPosition = glGetUniformLocation(m2DProgram, "uTexture");
m2DPositionAttributePosition = glGetAttribLocation(m2DProgram, "aPosition");
m2DTexCoordAttributePosition = glGetAttribLocation(m2DProgram, "aTexCoord");
mLevelZeroBlueInitData = createRGBInitData(getWindowWidth(), getWindowHeight(), 0, 0, 255); // Blue
mLevelZeroWhiteInitData = createRGBInitData(getWindowWidth(), getWindowHeight(), 255, 255, 255); // White
mLevelOneInitData = createRGBInitData((getWindowWidth() / 2), (getWindowHeight() / 2), 0, 255, 0); // Green
mLevelTwoInitData = createRGBInitData((getWindowWidth() / 4), (getWindowHeight() / 4), 255, 0, 0); // Red
glGenFramebuffers(1, &mOffscreenFramebuffer); glGenFramebuffers(1, &mOffscreenFramebuffer);
glGenTextures(1, &mOffscreenTexture); glGenTextures(1, &mOffscreenTexture2D);
// Initialize the texture to be empty, and don't use mips. // Initialize the texture2D to be empty, and don't use mips.
glBindTexture(GL_TEXTURE_2D, mOffscreenTexture); glBindTexture(GL_TEXTURE_2D, mOffscreenTexture2D);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
ASSERT_GL_NO_ERROR(); // Bind the texture2D to the offscreen framebuffer's color buffer.
// Bind the texture to the offscreen framebuffer's color buffer.
glBindFramebuffer(GL_FRAMEBUFFER, mOffscreenFramebuffer); glBindFramebuffer(GL_FRAMEBUFFER, mOffscreenFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mOffscreenTexture, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mOffscreenTexture2D, 0);
ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GL_FRAMEBUFFER_COMPLETE); ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GL_FRAMEBUFFER_COMPLETE);
mLevelZeroBlueInitData = createRGBInitData(getWindowWidth(), getWindowHeight(), 0, 0, 255); // Blue // Create a non-mipped texture cube. Set the negative-Y face to be blue.
mLevelZeroWhiteInitData = createRGBInitData(getWindowWidth(), getWindowHeight(), 255, 255, 255); // White glGenTextures(1, &mOffscreenTextureCube);
mLevelOneInitData = createRGBInitData((getWindowWidth() / 2), (getWindowHeight() / 2), 0, 255, 0); // Green glBindTexture(GL_TEXTURE_CUBE_MAP, mOffscreenTextureCube);
mLevelTwoInitData = createRGBInitData((getWindowWidth() / 4), (getWindowHeight() / 4), 255, 0, 0); // Red glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, mLevelZeroBlueInitData);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
// Complete the texture cube without mipmaps to start with.
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
ASSERT_GL_NO_ERROR();
} }
virtual void TearDown() virtual void TearDown()
{ {
glDeleteProgram(mProgram); glDeleteProgram(m2DProgram);
glDeleteProgram(mCubeProgram);
glDeleteFramebuffers(1, &mOffscreenFramebuffer); glDeleteFramebuffers(1, &mOffscreenFramebuffer);
glDeleteFramebuffers(1, &mOffscreenTexture); glDeleteTextures(1, &mOffscreenTexture2D);
glDeleteTextures(1, &mOffscreenTextureCube);
delete mLevelZeroBlueInitData; delete mLevelZeroBlueInitData;
delete mLevelZeroWhiteInitData; delete mLevelZeroWhiteInitData;
...@@ -135,28 +182,30 @@ class MipmapTest : public ANGLETest ...@@ -135,28 +182,30 @@ class MipmapTest : public ANGLETest
1.0f, 0.0f, 1.0f, 0.0f,
}; };
glUseProgram(mProgram); glUseProgram(m2DProgram);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(mTextureUniformPosition, 0); glUniform1i(m2DTextureUniformPosition, 0);
glVertexAttribPointer(mPositionAttributePosition, 3, GL_FLOAT, GL_FALSE, 0, vertexLocations); glVertexAttribPointer(m2DPositionAttributePosition, 3, GL_FLOAT, GL_FALSE, 0, vertexLocations);
glEnableVertexAttribArray(mPositionAttributePosition); glEnableVertexAttribArray(m2DPositionAttributePosition);
glVertexAttribPointer(mTexCoordAttributePosition, 2, GL_FLOAT, GL_FALSE, 0, vertexTexCoords); glVertexAttribPointer(m2DTexCoordAttributePosition, 2, GL_FLOAT, GL_FALSE, 0, vertexTexCoords);
glEnableVertexAttribArray(mTexCoordAttributePosition); glEnableVertexAttribArray(m2DTexCoordAttributePosition);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
} }
GLuint mProgram; GLuint m2DProgram;
GLuint mCubeProgram;
GLuint mOffscreenFramebuffer; GLuint mOffscreenFramebuffer;
GLuint mOffscreenTexture; GLuint mOffscreenTexture2D;
GLuint mOffscreenTextureCube;
GLint mTextureUniformPosition; GLint m2DTextureUniformPosition;
GLint mPositionAttributePosition; GLint m2DPositionAttributePosition;
GLint mTexCoordAttributePosition; GLint m2DTexCoordAttributePosition;
GLubyte* mLevelZeroBlueInitData; GLubyte* mLevelZeroBlueInitData;
GLubyte* mLevelZeroWhiteInitData; GLubyte* mLevelZeroWhiteInitData;
...@@ -262,7 +311,7 @@ protected: ...@@ -262,7 +311,7 @@ protected:
TYPED_TEST(MipmapTest, DISABLED_ThreeLevelsInitData) TYPED_TEST(MipmapTest, DISABLED_ThreeLevelsInitData)
{ {
// Pass in level zero init data. // Pass in level zero init data.
glBindTexture(GL_TEXTURE_2D, mOffscreenTexture); glBindTexture(GL_TEXTURE_2D, mOffscreenTexture2D);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, mLevelZeroBlueInitData); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, mLevelZeroBlueInitData);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
...@@ -270,15 +319,15 @@ TYPED_TEST(MipmapTest, DISABLED_ThreeLevelsInitData) ...@@ -270,15 +319,15 @@ TYPED_TEST(MipmapTest, DISABLED_ThreeLevelsInitData)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// Draw a full-sized quad, and check it's blue. // Draw a full-sized quad, and check it's blue.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight()); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255);
// Draw a half-sized quad, and check it's blue. // Draw a half-sized quad, and check it's blue.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 2, getWindowHeight() / 2); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 2, getWindowHeight() / 2);
EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 0, 0, 255, 255);
// Draw a quarter-sized quad, and check it's blue. // Draw a quarter-sized quad, and check it's blue.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 4, getWindowHeight() / 4); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 4, getWindowHeight() / 4);
EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 0, 0, 255, 255);
// Complete the texture by initializing the remaining levels. // Complete the texture by initializing the remaining levels.
...@@ -295,22 +344,22 @@ TYPED_TEST(MipmapTest, DISABLED_ThreeLevelsInitData) ...@@ -295,22 +344,22 @@ TYPED_TEST(MipmapTest, DISABLED_ThreeLevelsInitData)
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
// Draw a full-sized quad, and check it's blue. // Draw a full-sized quad, and check it's blue.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight()); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255);
// Draw a half-sized quad, and check it's blue. We've not enabled mipmaps yet, so our init data for level one shouldn't be used. // Draw a half-sized quad, and check it's blue. We've not enabled mipmaps yet, so our init data for level one shouldn't be used.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 2, getWindowHeight() / 2); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 2, getWindowHeight() / 2);
EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 0, 0, 255, 255);
// Enable mipmaps. // Enable mipmaps.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
// Draw a half-sized quad, and check it's green. // Draw a half-sized quad, and check it's green.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 2, getWindowHeight() / 2); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 2, getWindowHeight() / 2);
EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 0, 255, 0, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 0, 255, 0, 255);
// Draw a quarter-sized quad, and check it's black, since we've not passed any init data for level two. // Draw a quarter-sized quad, and check it's black, since we've not passed any init data for level two.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 4, getWindowHeight() / 4); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 4, getWindowHeight() / 4);
EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 0, 0, 0, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 0, 0, 0, 255);
// Pass in level two init data. // Pass in level two init data.
...@@ -318,45 +367,45 @@ TYPED_TEST(MipmapTest, DISABLED_ThreeLevelsInitData) ...@@ -318,45 +367,45 @@ TYPED_TEST(MipmapTest, DISABLED_ThreeLevelsInitData)
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
// Draw a full-sized quad, and check it's blue. // Draw a full-sized quad, and check it's blue.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight()); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255);
// Draw a half-sized quad, and check it's green. // Draw a half-sized quad, and check it's green.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 2, getWindowHeight() / 2); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 2, getWindowHeight() / 2);
EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 0, 255, 0, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 0, 255, 0, 255);
// Draw a quarter-sized quad, and check it's red. // Draw a quarter-sized quad, and check it's red.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 4, getWindowHeight() / 4); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 4, getWindowHeight() / 4);
EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 255, 0, 0, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 255, 0, 0, 255);
// Now disable mipmaps again, and render multiple sized quads. They should all be blue, since level 0 is blue. // Now disable mipmaps again, and render multiple sized quads. They should all be blue, since level 0 is blue.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight()); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255);
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 2, getWindowHeight() / 2); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 2, getWindowHeight() / 2);
EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 0, 0, 255, 255);
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 4, getWindowHeight() / 4); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 4, getWindowHeight() / 4);
EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 0, 0, 255, 255);
// Now reset level 0 to white, keeping mipmaps disabled. Then, render various sized quads. They should be white. // Now reset level 0 to white, keeping mipmaps disabled. Then, render various sized quads. They should be white.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, mLevelZeroWhiteInitData); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, mLevelZeroWhiteInitData);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight()); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 255, 255, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 255, 255, 255, 255);
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 2, getWindowHeight() / 2); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 2, getWindowHeight() / 2);
EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 255, 255, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 255, 255, 255, 255);
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 4, getWindowHeight() / 4); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 4, getWindowHeight() / 4);
EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 255, 255, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 255, 255, 255, 255);
// Then enable mipmaps again. The quads should be white, green, red respectively. // Then enable mipmaps again. The quads should be white, green, red respectively.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight()); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 255, 255, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 255, 255, 255, 255);
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 2, getWindowHeight() / 2); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 2, getWindowHeight() / 2);
EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 0, 255, 0, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 0, 255, 0, 255);
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 4, getWindowHeight() / 4); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 4, getWindowHeight() / 4);
EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 255, 0, 0, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 255, 0, 0, 255);
} }
...@@ -378,22 +427,22 @@ TYPED_TEST(MipmapTest, GenerateMipmapFromInitDataThenRender) ...@@ -378,22 +427,22 @@ TYPED_TEST(MipmapTest, GenerateMipmapFromInitDataThenRender)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
// Now draw the texture to various different sized areas. // Now draw the texture to various different sized areas.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight()); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255);
// Use mip level 1 // Use mip level 1
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 2, getWindowHeight() / 2); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 2, getWindowHeight() / 2);
EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 0, 0, 255, 255);
// Use mip level 2 // Use mip level 2
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 4, getWindowHeight() / 4); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 4, getWindowHeight() / 4);
EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 0, 0, 255, 255);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
// Disable mips. Render a quad using the texture and ensure it's blue. // Disable mips. Render a quad using the texture and ensure it's blue.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight()); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255);
// Clear level 0 of the texture. // Clear level 0 of the texture.
...@@ -405,15 +454,15 @@ TYPED_TEST(MipmapTest, GenerateMipmapFromInitDataThenRender) ...@@ -405,15 +454,15 @@ TYPED_TEST(MipmapTest, GenerateMipmapFromInitDataThenRender)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
// Level 0 is now red, so this should render red. // Level 0 is now red, so this should render red.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight()); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 255, 0, 0, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 255, 0, 0, 255);
// Use mip level 1, blue. // Use mip level 1, blue.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 2, getWindowHeight() / 2); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 2, getWindowHeight() / 2);
EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 0, 0, 255, 255);
// Use mip level 2, blue. // Use mip level 2, blue.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 4, getWindowHeight() / 4); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 4, getWindowHeight() / 4);
EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 0, 0, 255, 255);
} }
...@@ -437,15 +486,15 @@ TYPED_TEST(MipmapTest, GenerateMipmapFromRenderedImage) ...@@ -437,15 +486,15 @@ TYPED_TEST(MipmapTest, GenerateMipmapFromRenderedImage)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
// Now draw the texture to various different sized areas. // Now draw the texture to various different sized areas.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight()); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255);
// Use mip level 1 // Use mip level 1
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 2, getWindowHeight() / 2); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 2, getWindowHeight() / 2);
EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 4, getWindowHeight() / 4, 0, 0, 255, 255);
// Use mip level 2 // Use mip level 2
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 4, getWindowHeight() / 4); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 4, getWindowHeight() / 4);
EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 0, 0, 255, 255);
} }
...@@ -465,7 +514,7 @@ TYPED_TEST(MipmapTest, RenderOntoLevelZeroAfterGenerateMipmap) ...@@ -465,7 +514,7 @@ TYPED_TEST(MipmapTest, RenderOntoLevelZeroAfterGenerateMipmap)
// Now, draw the texture to a quad that's the same size as the texture. This draws to the default framebuffer. // Now, draw the texture to a quad that's the same size as the texture. This draws to the default framebuffer.
// The quad should be blue. // The quad should be blue.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight()); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255);
// Now go back to the texture, and generate mips on it. // Now go back to the texture, and generate mips on it.
...@@ -474,7 +523,7 @@ TYPED_TEST(MipmapTest, RenderOntoLevelZeroAfterGenerateMipmap) ...@@ -474,7 +523,7 @@ TYPED_TEST(MipmapTest, RenderOntoLevelZeroAfterGenerateMipmap)
// Now try rendering the textured quad again. Note: we've not told GL to use the generated mips. // Now try rendering the textured quad again. Note: we've not told GL to use the generated mips.
// The quad should be blue. // The quad should be blue.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight()); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255);
// Now tell GL to use the generated mips. // Now tell GL to use the generated mips.
...@@ -482,11 +531,11 @@ TYPED_TEST(MipmapTest, RenderOntoLevelZeroAfterGenerateMipmap) ...@@ -482,11 +531,11 @@ TYPED_TEST(MipmapTest, RenderOntoLevelZeroAfterGenerateMipmap)
EXPECT_EQ(glGetError(), GL_NONE); EXPECT_EQ(glGetError(), GL_NONE);
// Now render the textured quad again. It should be still be blue. // Now render the textured quad again. It should be still be blue.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight()); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255);
// Now render the textured quad to an area smaller than the texture (i.e. to force minification). This should be blue. // Now render the textured quad to an area smaller than the texture (i.e. to force minification). This should be blue.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 4, getWindowHeight() / 4); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 4, getWindowHeight() / 4);
EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 0, 0, 255, 255);
// Now clear the texture to green. This just clears the top level. The lower mips should remain blue. // Now clear the texture to green. This just clears the top level. The lower mips should remain blue.
...@@ -498,11 +547,11 @@ TYPED_TEST(MipmapTest, RenderOntoLevelZeroAfterGenerateMipmap) ...@@ -498,11 +547,11 @@ TYPED_TEST(MipmapTest, RenderOntoLevelZeroAfterGenerateMipmap)
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// Render a textured quad equal in size to the texture. This should be green, since we just cleared level 0. // Render a textured quad equal in size to the texture. This should be green, since we just cleared level 0.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight()); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 255, 0, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 255, 0, 255);
// Render a small textured quad. This forces minification, so should render blue (the color of levels 1+). // Render a small textured quad. This forces minification, so should render blue (the color of levels 1+).
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 4, getWindowHeight() / 4); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 4, getWindowHeight() / 4);
EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 0, 0, 255, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 0, 0, 255, 255);
// Disable mipmaps again // Disable mipmaps again
...@@ -510,14 +559,144 @@ TYPED_TEST(MipmapTest, RenderOntoLevelZeroAfterGenerateMipmap) ...@@ -510,14 +559,144 @@ TYPED_TEST(MipmapTest, RenderOntoLevelZeroAfterGenerateMipmap)
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
// Render a textured quad equal in size to the texture. This should be green, the color of level 0 in the texture. // Render a textured quad equal in size to the texture. This should be green, the color of level 0 in the texture.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight()); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 255, 0, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 255, 0, 255);
// Render a small textured quad. This would force minification if mips were enabled, but they're not. Therefore, this should be green. // Render a small textured quad. This would force minification if mips were enabled, but they're not. Therefore, this should be green.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 4, getWindowHeight() / 4); ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 4, getWindowHeight() / 4);
EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 0, 255, 0, 255); EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 0, 255, 0, 255);
} }
// This test ensures that the level-zero workaround for TextureCubes (on D3D11 Feature Level 9_3)
// works as expected. It tests enabling/disabling mipmaps, generating mipmaps, and rendering to level zero.
TYPED_TEST(MipmapTest, TextureCubeGeneralLevelZero)
{
GLfloat vertexLocations[] =
{
-1.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
};
// Set up the viewport, program, attributes, sampler and texture for the cube
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, getWindowWidth(), getWindowHeight());
glUseProgram(mCubeProgram);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertexLocations);
glEnableVertexAttribArray(0);
glUniform1i(0, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, mOffscreenTextureCube);
// Draw. Since the negative-Y face's is blue, this should be blue.
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EXPECT_PIXEL_EQ(0, 0, 0, 0, 255, 255);
// Generate mipmaps, and render. This should be blue.
glClear(GL_COLOR_BUFFER_BIT);
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EXPECT_PIXEL_EQ(0, 0, 0, 0, 255, 255);
// Draw using a smaller viewport (to force a lower LOD of the texture). This should still be blue.
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, getWindowWidth() / 4, getWindowHeight() / 4);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EXPECT_PIXEL_EQ(0, 0, 0, 0, 255, 255);
// Now clear the negative-Y face of the cube to red.
GLuint mOffscreenFramebuffer;
glGenFramebuffers(0, &mOffscreenFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, mOffscreenFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, mOffscreenTextureCube, 0);
ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GL_FRAMEBUFFER_COMPLETE);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Switch back to the default framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_CUBE_MAP, mOffscreenTextureCube);
// Draw using a full-size viewport. This should be red.
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, getWindowWidth(), getWindowHeight());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
// Draw using a quarter-size viewport, to force a lower LOD. This should be *BLUE*, since we only cleared level zero
// of the negative-Y face to red, and left its mipmaps blue.
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, getWindowWidth() / 4, getWindowHeight() / 4);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EXPECT_PIXEL_EQ(0, 0, 0, 0, 255, 255);
// Disable mipmaps again, and draw a to a quarter-size viewport.
// Since this should use level zero of the texture, this should be *RED*.
glClear(GL_COLOR_BUFFER_BIT);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glViewport(0, 0, getWindowWidth() / 4, getWindowHeight() / 4);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
// This test ensures that rendering to level-zero of a TextureCube works as expected.
TYPED_TEST(MipmapTest, TextureCubeRenderToLevelZero)
{
GLfloat vertexLocations[] =
{
-1.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
};
// Set up the viewport, program, attributes, sampler and texture for the cube
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, getWindowWidth(), getWindowHeight());
glUseProgram(mCubeProgram);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertexLocations);
glEnableVertexAttribArray(0);
glUniform1i(0, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, mOffscreenTextureCube);
// Draw. Since the negative-Y face's is blue, this should be blue.
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EXPECT_PIXEL_EQ(0, 0, 0, 0, 255, 255);
// Now clear the negative-Y face of the cube to red.
GLuint mOffscreenFramebuffer;
glGenFramebuffers(0, &mOffscreenFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, mOffscreenFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, mOffscreenTextureCube, 0);
ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GL_FRAMEBUFFER_COMPLETE);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Switch back to the default framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_CUBE_MAP, mOffscreenTextureCube);
// Draw using a full-size viewport. This should be red.
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, getWindowWidth(), getWindowHeight());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
// Draw a to a quarter-size viewport. This should also be red.
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, getWindowWidth() / 4, getWindowHeight() / 4);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
}
// Creates a mipmapped 2D array texture with three layers, and calls ANGLE's GenerateMipmap. // Creates a mipmapped 2D array texture with three layers, and calls ANGLE's GenerateMipmap.
// Then tests if the mipmaps are rendered correctly for all three layers. // Then tests if the mipmaps are rendered correctly for all three layers.
TYPED_TEST(MipmapTestES3, MipmapsForTextureArray) TYPED_TEST(MipmapTestES3, MipmapsForTextureArray)
......
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