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
virtual gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) = 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 *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 *createTextureStorage2DArray(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels) = 0;
......
......@@ -352,9 +352,6 @@ gl::Error TextureD3D::generateMipmaps()
return gl::Error(GL_NO_ERROR); // no-op
}
// Set up proper mipmap chain in our Image array.
initMipmapsImages();
if (mTexStorage && mRenderer->getWorkarounds().zeroMaxLodWorkaround)
{
// Switch to using the mipmapped texture.
......@@ -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
GLint layerCount = static_cast<GLint>(getLayerCount(0));
......@@ -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::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);
if (error.isError())
......@@ -771,7 +773,9 @@ gl::Error TextureD3D_2D::copySubImage(GLenum target, size_t level, const gl::Off
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);
if (error.isError())
......@@ -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.
// Otherwise, it should use the level-zero-only texture.
hintLevelZeroOnly = true;
int level = 1;
while (hintLevelZeroOnly && level < levels)
for (int level = 1; level < levels && hintLevelZeroOnly; 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
// TODO(geofflang): Verify storage creation had no errors
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);
if (error.isError())
......@@ -1524,8 +1527,23 @@ gl::Error TextureD3D_Cube::createCompleteStorage(bool renderTarget, TextureStora
// use existing storage level count, when previously specified by TexStorage*D
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
*outTexStorage = mRenderer->createTextureStorageCube(getBaseLevelInternalFormat(), renderTarget, size, levels);
*outTexStorage = mRenderer->createTextureStorageCube(getBaseLevelInternalFormat(), renderTarget, size, levels, hintLevelZeroOnly);
return gl::Error(GL_NO_ERROR);
}
......
......@@ -2829,9 +2829,9 @@ TextureStorage *Renderer11::createTextureStorage2D(GLenum internalformat, bool r
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)
......
......@@ -155,7 +155,7 @@ class Renderer11 : public RendererD3D
gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) override;
virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain);
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 *createTextureStorage2DArray(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels);
......
......@@ -426,8 +426,28 @@ gl::Error TextureStorage11::copySubresourceLevel(ID3D11Resource* dstTexture, uns
unsigned int srcSubresource = getSubresourceIndex(index);
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,
srcTexture, srcSubresource, NULL);
srcTexture, srcSubresource, pSrcBox);
return gl::Error(GL_NO_ERROR);
}
......@@ -662,7 +682,7 @@ TextureStorage11_2D::TextureStorage11_2D(Renderer11 *renderer, GLenum internalfo
mTextureHeight = height;
mTextureDepth = 1;
if (hintLevelZeroOnly)
if (hintLevelZeroOnly && levels > 1)
{
//The LevelZeroOnly hint should only be true if the zero max LOD workaround is active.
ASSERT(mRenderer->getWorkarounds().zeroMaxLodWorkaround);
......@@ -787,7 +807,7 @@ gl::Error TextureStorage11_2D::copyToStorage(TextureStorage *destStorage)
gl::Error TextureStorage11_2D::useLevelZeroWorkaroundTexture(bool useLevelZeroTexture)
{
if (useLevelZeroTexture)
if (useLevelZeroTexture && mMipLevels > 1)
{
if (!mUseLevelZeroTexture && mTexture)
{
......@@ -798,8 +818,8 @@ gl::Error TextureStorage11_2D::useLevelZeroWorkaroundTexture(bool useLevelZeroTe
}
// Pull data back from the mipped texture if necessary.
ASSERT(mTexture);
ID3D11DeviceContext* context = mRenderer->getDeviceContext();
ASSERT(mLevelZeroTexture);
ID3D11DeviceContext *context = mRenderer->getDeviceContext();
context->CopySubresourceRegion(mLevelZeroTexture, 0, 0, 0, 0, mTexture, 0, NULL);
}
......@@ -817,7 +837,7 @@ gl::Error TextureStorage11_2D::useLevelZeroWorkaroundTexture(bool useLevelZeroTe
// Pull data back from the level zero texture if necessary.
ASSERT(mTexture);
ID3D11DeviceContext* context = mRenderer->getDeviceContext();
ID3D11DeviceContext *context = mRenderer->getDeviceContext();
context->CopySubresourceRegion(mTexture, 0, 0, 0, 0, mLevelZeroTexture, 0, NULL);
}
......@@ -906,7 +926,7 @@ gl::Error TextureStorage11_2D::releaseAssociatedImage(const gl::ImageIndex &inde
gl::Error TextureStorage11_2D::getResource(ID3D11Resource **outResource)
{
if (mUseLevelZeroTexture)
if (mUseLevelZeroTexture && mMipLevels > 1)
{
gl::Error error = ensureTextureExists(1);
if (error.isError())
......@@ -947,7 +967,9 @@ gl::Error TextureStorage11_2D::getMippedResource(ID3D11Resource **outResource)
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
// 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
ASSERT(baseLevel == 0);
// 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.
ASSERT(mLevelZeroTexture != NULL && texture == mLevelZeroTexture);
......@@ -1220,7 +1242,7 @@ gl::Error TextureStorage11_2D::getSwizzleRenderTarget(int mipLevel, ID3D11Render
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))
{
mTexture = NULL;
......@@ -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;
const d3d11::TextureFormat &formatInfo = d3d11::GetTextureFormatInfo(internalformat, renderer->getFeatureLevel());
......@@ -1256,6 +1286,13 @@ TextureStorage11_Cube::TextureStorage11_Cube(Renderer11 *renderer, GLenum intern
mTextureHeight = size;
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);
}
......@@ -1281,6 +1318,12 @@ TextureStorage11_Cube::~TextureStorage11_Cube()
SafeRelease(mTexture);
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++)
{
......@@ -1298,6 +1341,147 @@ TextureStorage11_Cube *TextureStorage11_Cube::makeTextureStorage11_Cube(TextureS
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)
{
GLint level = index.mipIndex;
......@@ -1396,9 +1580,54 @@ gl::Error TextureStorage11_Cube::releaseAssociatedImage(const gl::ImageIndex &in
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
// 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);
......@@ -1407,7 +1636,7 @@ gl::Error TextureStorage11_Cube::getResource(ID3D11Resource **outResource)
D3D11_TEXTURE2D_DESC desc;
desc.Width = mTextureWidth;
desc.Height = mTextureHeight;
desc.MipLevels = mMipLevels;
desc.MipLevels = mipLevels;
desc.ArraySize = CUBE_FACE_COUNT;
desc.Format = mTextureFormat;
desc.SampleDesc.Count = 1;
......@@ -1417,7 +1646,7 @@ gl::Error TextureStorage11_Cube::getResource(ID3D11Resource **outResource)
desc.CPUAccessFlags = 0;
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
if (d3d11::isDeviceLostError(result))
......@@ -1432,7 +1661,6 @@ gl::Error TextureStorage11_Cube::getResource(ID3D11Resource **outResource)
}
}
*outResource = mTexture;
return gl::Error(GL_NO_ERROR);
}
......@@ -1456,14 +1684,53 @@ gl::Error TextureStorage11_Cube::getRenderTarget(const gl::ImageIndex &index, Re
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;
srvDesc.Format = mShaderResourceFormat;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; // Will be used with Texture2D sampler, not TextureCube
srvDesc.Texture2DArray.MostDetailedMip = mTopLevel + level;
srvDesc.Texture2DArray.MipLevels = 1;
srvDesc.Texture2DArray.FirstArraySlice = faceIndex;
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;
result = device->CreateShaderResourceView(texture, &srvDesc, &srv);
......@@ -1560,8 +1827,30 @@ gl::Error TextureStorage11_Cube::createSRV(int baseLevel, int mipLevels, DXGI_FO
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();
HRESULT result = device->CreateShaderResourceView(texture, &srvDesc, outSRV);
HRESULT result = device->CreateShaderResourceView(srvTexture, &srvDesc, outSRV);
ASSERT(result == E_OUTOFMEMORY || SUCCEEDED(result));
if (FAILED(result))
......
......@@ -50,7 +50,7 @@ class TextureStorage11 : public TextureStorage
virtual bool isRenderTarget() const;
virtual bool isManaged() 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);
void invalidateSwizzleCacheLevel(int mipLevel);
......@@ -201,23 +201,32 @@ class TextureStorage11_2D : public TextureStorage11
class TextureStorage11_Cube : public TextureStorage11
{
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();
static TextureStorage11_Cube *makeTextureStorage11_Cube(TextureStorage *storage);
virtual UINT getSubresourceIndex(const gl::ImageIndex &index) const;
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 copyToStorage(TextureStorage *destStorage);
virtual void associateImage(Image11* image, const gl::ImageIndex &index);
virtual void disassociateImage(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 useLevelZeroWorkaroundTexture(bool useLevelZeroTexture);
protected:
virtual gl::Error getSwizzleTexture(ID3D11Resource **outTexture);
virtual gl::Error getSwizzleRenderTarget(int mipLevel, ID3D11RenderTargetView **outRTV);
gl::Error ensureTextureExists(int mipLevels);
private:
DISALLOW_COPY_AND_ASSIGN(TextureStorage11_Cube);
......@@ -229,6 +238,11 @@ class TextureStorage11_Cube : public TextureStorage11
ID3D11Texture2D *mTexture;
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;
ID3D11RenderTargetView *mSwizzleRenderTargets[gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS];
......
......@@ -2832,9 +2832,9 @@ TextureStorage *Renderer9::createTextureStorage2D(GLenum internalformat, bool re
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)
......
......@@ -164,7 +164,7 @@ class Renderer9 : public RendererD3D
gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) override;
virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain);
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 *createTextureStorage2DArray(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels);
......
......@@ -286,7 +286,7 @@ gl::Error TextureStorage9_2D::copyToStorage(TextureStorage *destStorage)
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))
{
mTexture = NULL;
......
......@@ -88,7 +88,7 @@ class TextureStorage9_2D : public TextureStorage9
class TextureStorage9_Cube : public TextureStorage9
{
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();
static TextureStorage9_Cube *makeTextureStorage9_Cube(TextureStorage *storage);
......
#include "ANGLETest.h"
// 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>
class CubeMapTextureTest : public ANGLETest
......
......@@ -50,43 +50,90 @@ class MipmapTest : public ANGLETest
}
);
mProgram = CompileProgram(vs, fs);
if (mProgram == 0)
m2DProgram = CompileProgram(vs, fs);
if (m2DProgram == 0)
{
FAIL() << "shader compilation failed.";
}
mTextureUniformPosition = glGetUniformLocation(mProgram, "uTexture");
mPositionAttributePosition = glGetAttribLocation(mProgram, "aPosition");
mTexCoordAttributePosition = glGetAttribLocation(mProgram, "aTexCoord");
// A simple vertex shader for the texture cube
const std::string cubeVS = SHADER_SOURCE
(
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);
glGenTextures(1, &mOffscreenTexture);
glGenTextures(1, &mOffscreenTexture2D);
// Initialize the texture to be empty, and don't use mips.
glBindTexture(GL_TEXTURE_2D, mOffscreenTexture);
// Initialize the texture2D to be empty, and don't use mips.
glBindTexture(GL_TEXTURE_2D, mOffscreenTexture2D);
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_MIN_FILTER, GL_LINEAR);
ASSERT_GL_NO_ERROR();
// Bind the texture to the offscreen framebuffer's color buffer.
// Bind the texture2D to the offscreen framebuffer's color buffer.
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);
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
// Create a non-mipped texture cube. Set the negative-Y face to be blue.
glGenTextures(1, &mOffscreenTextureCube);
glBindTexture(GL_TEXTURE_CUBE_MAP, mOffscreenTextureCube);
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()
{
glDeleteProgram(mProgram);
glDeleteProgram(m2DProgram);
glDeleteProgram(mCubeProgram);
glDeleteFramebuffers(1, &mOffscreenFramebuffer);
glDeleteFramebuffers(1, &mOffscreenTexture);
glDeleteTextures(1, &mOffscreenTexture2D);
glDeleteTextures(1, &mOffscreenTextureCube);
delete mLevelZeroBlueInitData;
delete mLevelZeroWhiteInitData;
......@@ -135,28 +182,30 @@ class MipmapTest : public ANGLETest
1.0f, 0.0f,
};
glUseProgram(mProgram);
glUseProgram(m2DProgram);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(mTextureUniformPosition, 0);
glUniform1i(m2DTextureUniformPosition, 0);
glVertexAttribPointer(mPositionAttributePosition, 3, GL_FLOAT, GL_FALSE, 0, vertexLocations);
glEnableVertexAttribArray(mPositionAttributePosition);
glVertexAttribPointer(m2DPositionAttributePosition, 3, GL_FLOAT, GL_FALSE, 0, vertexLocations);
glEnableVertexAttribArray(m2DPositionAttributePosition);
glVertexAttribPointer(mTexCoordAttributePosition, 2, GL_FLOAT, GL_FALSE, 0, vertexTexCoords);
glEnableVertexAttribArray(mTexCoordAttributePosition);
glVertexAttribPointer(m2DTexCoordAttributePosition, 2, GL_FLOAT, GL_FALSE, 0, vertexTexCoords);
glEnableVertexAttribArray(m2DTexCoordAttributePosition);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
GLuint mProgram;
GLuint m2DProgram;
GLuint mCubeProgram;
GLuint mOffscreenFramebuffer;
GLuint mOffscreenTexture;
GLuint mOffscreenTexture2D;
GLuint mOffscreenTextureCube;
GLint mTextureUniformPosition;
GLint mPositionAttributePosition;
GLint mTexCoordAttributePosition;
GLint m2DTextureUniformPosition;
GLint m2DPositionAttributePosition;
GLint m2DTexCoordAttributePosition;
GLubyte* mLevelZeroBlueInitData;
GLubyte* mLevelZeroWhiteInitData;
......@@ -262,7 +311,7 @@ protected:
TYPED_TEST(MipmapTest, DISABLED_ThreeLevelsInitData)
{
// 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);
ASSERT_GL_NO_ERROR();
......@@ -270,15 +319,15 @@ TYPED_TEST(MipmapTest, DISABLED_ThreeLevelsInitData)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// 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);
// 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);
// 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);
// Complete the texture by initializing the remaining levels.
......@@ -295,22 +344,22 @@ TYPED_TEST(MipmapTest, DISABLED_ThreeLevelsInitData)
ASSERT_GL_NO_ERROR();
// 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);
// 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);
// Enable mipmaps.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
// 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);
// 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);
// Pass in level two init data.
......@@ -318,45 +367,45 @@ TYPED_TEST(MipmapTest, DISABLED_ThreeLevelsInitData)
ASSERT_GL_NO_ERROR();
// 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);
// 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);
// 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);
// 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);
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight());
ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
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);
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 4, getWindowHeight() / 4);
ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 4, getWindowHeight() / 4);
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.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, mLevelZeroWhiteInitData);
ASSERT_GL_NO_ERROR();
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight());
ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
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);
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 4, getWindowHeight() / 4);
ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 4, getWindowHeight() / 4);
EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 255, 255, 255, 255);
// Then enable mipmaps again. The quads should be white, green, red respectively.
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);
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 2, getWindowHeight() / 2);
ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 2, getWindowHeight() / 2);
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);
}
......@@ -378,22 +427,22 @@ TYPED_TEST(MipmapTest, GenerateMipmapFromInitDataThenRender)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
// 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);
// 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);
// 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);
ASSERT_GL_NO_ERROR();
// Disable mips. Render a quad using the texture and ensure it's blue.
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);
// Clear level 0 of the texture.
......@@ -405,15 +454,15 @@ TYPED_TEST(MipmapTest, GenerateMipmapFromInitDataThenRender)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
// 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);
// 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);
// 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);
}
......@@ -437,15 +486,15 @@ TYPED_TEST(MipmapTest, GenerateMipmapFromRenderedImage)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
// 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);
// 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);
// 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);
}
......@@ -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.
// The quad should be blue.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight());
ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255);
// Now go back to the texture, and generate mips on it.
......@@ -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.
// The quad should be blue.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight());
ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 255, 255);
// Now tell GL to use the generated mips.
......@@ -482,11 +531,11 @@ TYPED_TEST(MipmapTest, RenderOntoLevelZeroAfterGenerateMipmap)
EXPECT_EQ(glGetError(), GL_NONE);
// 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);
// 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);
// 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)
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.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight());
ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
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+).
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 4, getWindowHeight() / 4);
ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 4, getWindowHeight() / 4);
EXPECT_PIXEL_EQ(getWindowWidth() / 8, getWindowHeight() / 8, 0, 0, 255, 255);
// Disable mipmaps again
......@@ -510,14 +559,144 @@ TYPED_TEST(MipmapTest, RenderOntoLevelZeroAfterGenerateMipmap)
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.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth(), getWindowHeight());
ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth(), getWindowHeight());
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.
ClearAndDrawTexturedQuad(mOffscreenTexture, getWindowWidth() / 4, getWindowHeight() / 4);
ClearAndDrawTexturedQuad(mOffscreenTexture2D, getWindowWidth() / 4, getWindowHeight() / 4);
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.
// Then tests if the mipmaps are rendered correctly for all three layers.
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