Commit 1fbc59fe by Jamie Madill

D3D11: Enable dirty bits for Framebuffer11.

This patch works using a notification scheme - whenever a Texture or Renderbuffer changes in such a way as to recreate its RenderTarget, we pass a signal to the Framebuffer to invalidate some internal state. Everything is entirely tracked in the Renderer11 layer, and the GL layer is left untouched. A RenderTarget11 now tracks points to which it is bound, and the Framebuffer11 is mostly responsible for managing those links. The three locations where we notify a Framebuffer when its bound RenderTargets might be dirty are: 1) RenderTarget11::~RenderTarget 2) EGLImageD3D::copyToLocalRendertarget 3) TextureStorage11_2D::useLevelZeroWorkaroundTexture This patch gives about a 10% score increase in the D3D11 draw call benchmark on my system. BUG=angleproject:1260 Change-Id: Ide38aeadff4a2681bf5bd685e8ca3c9e2612a380 Reviewed-on: https://chromium-review.googlesource.com/327255Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent d834e3dc
...@@ -19,6 +19,7 @@ base_path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file_ ...@@ -19,6 +19,7 @@ base_path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file_
# You might have to re-order these to find the specific version you want. # You might have to re-order these to find the specific version you want.
perftests_paths = [ perftests_paths = [
os.path.join('out', 'Release_x64'),
os.path.join('out', 'Release'), os.path.join('out', 'Release'),
os.path.join('build', 'Release_x64'), os.path.join('build', 'Release_x64'),
os.path.join('build', 'Release_Win32') os.path.join('build', 'Release_Win32')
......
...@@ -299,6 +299,11 @@ GLenum Framebuffer::getDrawBufferState(size_t drawBuffer) const ...@@ -299,6 +299,11 @@ GLenum Framebuffer::getDrawBufferState(size_t drawBuffer) const
return mData.mDrawBufferStates[drawBuffer]; return mData.mDrawBufferStates[drawBuffer];
} }
const std::vector<GLenum> &Framebuffer::getDrawBufferStates() const
{
return mData.getDrawBufferStates();
}
void Framebuffer::setDrawBuffers(size_t count, const GLenum *buffers) void Framebuffer::setDrawBuffers(size_t count, const GLenum *buffers)
{ {
auto &drawStates = mData.mDrawBufferStates; auto &drawStates = mData.mDrawBufferStates;
......
...@@ -119,6 +119,7 @@ class Framebuffer final : public LabeledObject ...@@ -119,6 +119,7 @@ class Framebuffer final : public LabeledObject
size_t getDrawbufferStateCount() const; size_t getDrawbufferStateCount() const;
GLenum getDrawBufferState(size_t drawBuffer) const; GLenum getDrawBufferState(size_t drawBuffer) const;
const std::vector<GLenum> &getDrawBufferStates() const;
void setDrawBuffers(size_t count, const GLenum *buffers); void setDrawBuffers(size_t count, const GLenum *buffers);
const FramebufferAttachment *getDrawBuffer(size_t drawBuffer) const; const FramebufferAttachment *getDrawBuffer(size_t drawBuffer) const;
bool hasEnabledDrawBuffer() const; bool hasEnabledDrawBuffer() const;
......
...@@ -123,6 +123,9 @@ gl::Error EGLImageD3D::copyToLocalRendertarget() ...@@ -123,6 +123,9 @@ gl::Error EGLImageD3D::copyToLocalRendertarget()
return error; return error;
} }
// This only currently applies do D3D11, where it invalidates FBOs with this Image attached.
curRenderTarget->signalDirty();
// Clear the source image buffers // Clear the source image buffers
mBuffer = nullptr; mBuffer = nullptr;
mAttachmentBuffer = nullptr; mAttachmentBuffer = nullptr;
......
...@@ -33,6 +33,9 @@ class RenderTargetD3D : public FramebufferAttachmentRenderTarget ...@@ -33,6 +33,9 @@ class RenderTargetD3D : public FramebufferAttachmentRenderTarget
virtual unsigned int getSerial() const; virtual unsigned int getSerial() const;
static unsigned int issueSerials(unsigned int count); static unsigned int issueSerials(unsigned int count);
// Only currently applies to D3D11.
virtual void signalDirty() {}
private: private:
const unsigned int mSerial; const unsigned int mSerial;
static unsigned int mCurrentSerial; static unsigned int mCurrentSerial;
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "libANGLE/renderer/d3d/d3d11/Framebuffer11.h" #include "libANGLE/renderer/d3d/d3d11/Framebuffer11.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/BitSetIterator.h"
#include "libANGLE/renderer/d3d/d3d11/Buffer11.h" #include "libANGLE/renderer/d3d/d3d11/Buffer11.h"
#include "libANGLE/renderer/d3d/d3d11/Clear11.h" #include "libANGLE/renderer/d3d/d3d11/Clear11.h"
#include "libANGLE/renderer/d3d/d3d11/TextureStorage11.h" #include "libANGLE/renderer/d3d/d3d11/TextureStorage11.h"
...@@ -24,17 +25,9 @@ ...@@ -24,17 +25,9 @@
namespace rx namespace rx
{ {
Framebuffer11::Framebuffer11(const gl::Framebuffer::Data &data, Renderer11 *renderer) namespace
: FramebufferD3D(data, renderer), mRenderer(renderer)
{
ASSERT(mRenderer != nullptr);
}
Framebuffer11::~Framebuffer11()
{ {
} gl::Error InvalidateAttachmentSwizzles(const gl::FramebufferAttachment *attachment)
static gl::Error InvalidateAttachmentSwizzles(const gl::FramebufferAttachment *attachment)
{ {
if (attachment && attachment->type() == GL_TEXTURE) if (attachment && attachment->type() == GL_TEXTURE)
{ {
...@@ -61,6 +54,67 @@ static gl::Error InvalidateAttachmentSwizzles(const gl::FramebufferAttachment *a ...@@ -61,6 +54,67 @@ static gl::Error InvalidateAttachmentSwizzles(const gl::FramebufferAttachment *a
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
void UpdateCachedRenderTarget(const gl::FramebufferAttachment *attachment,
RenderTarget11 *&cachedRenderTarget,
const NotificationCallback &callbackFunc)
{
RenderTarget11 *newRenderTarget = nullptr;
if (attachment)
{
attachment->getRenderTarget(&newRenderTarget);
}
if (newRenderTarget != cachedRenderTarget)
{
if (cachedRenderTarget)
{
cachedRenderTarget->removeDirtyCallback(&callbackFunc);
}
if (newRenderTarget)
{
newRenderTarget->addDirtyCallback(&callbackFunc);
}
cachedRenderTarget = newRenderTarget;
}
}
} // anonymous namespace
Framebuffer11::Framebuffer11(const gl::Framebuffer::Data &data, Renderer11 *renderer)
: FramebufferD3D(data, renderer), mRenderer(renderer), mCachedDepthStencilRenderTarget(nullptr)
{
ASSERT(mRenderer != nullptr);
mCachedColorRenderTargets.fill(nullptr);
for (size_t colorIndex = 0; colorIndex < data.getColorAttachments().size(); ++colorIndex)
{
auto callback = [this, colorIndex]()
{
this->markColorRenderTargetDirty(colorIndex);
};
mColorRenderTargetsDirty.push_back(callback);
}
mDepthStencilRenderTargetDirty = [this]()
{
this->markDepthStencilRenderTargetDirty();
};
}
Framebuffer11::~Framebuffer11()
{
for (size_t colorIndex = 0; colorIndex < mCachedColorRenderTargets.size(); ++colorIndex)
{
auto *colorRenderTarget = mCachedColorRenderTargets[colorIndex];
if (colorRenderTarget)
{
colorRenderTarget->removeDirtyCallback(&mColorRenderTargetsDirty[colorIndex]);
}
}
if (mCachedDepthStencilRenderTarget)
{
mCachedDepthStencilRenderTarget->removeDirtyCallback(&mDepthStencilRenderTargetDirty);
}
}
gl::Error Framebuffer11::invalidateSwizzles() const gl::Error Framebuffer11::invalidateSwizzles() const
{ {
for (const auto &colorAttachment : mData.getColorAttachments()) for (const auto &colorAttachment : mData.getColorAttachments())
...@@ -425,4 +479,77 @@ GLenum Framebuffer11::getRenderTargetImplementationFormat(RenderTargetD3D *rende ...@@ -425,4 +479,77 @@ GLenum Framebuffer11::getRenderTargetImplementationFormat(RenderTargetD3D *rende
return dxgiFormatInfo.internalFormat; return dxgiFormatInfo.internalFormat;
} }
void Framebuffer11::updateColorRenderTarget(size_t colorIndex)
{
UpdateCachedRenderTarget(mData.getColorAttachment(colorIndex),
mCachedColorRenderTargets[colorIndex],
mColorRenderTargetsDirty[colorIndex]);
}
void Framebuffer11::updateDepthStencilRenderTarget()
{
UpdateCachedRenderTarget(mData.getDepthOrStencilAttachment(), mCachedDepthStencilRenderTarget,
mDepthStencilRenderTargetDirty);
}
void Framebuffer11::syncState(const gl::Framebuffer::DirtyBits &dirtyBits)
{
mRenderer->getStateManager()->invalidateRenderTarget();
const auto &mergedDirtyBits = dirtyBits | mInternalDirtyBits;
mInternalDirtyBits.reset();
for (auto dirtyBit : angle::IterateBitSet(mergedDirtyBits))
{
switch (dirtyBit)
{
case gl::Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT:
case gl::Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT:
updateDepthStencilRenderTarget();
break;
case gl::Framebuffer::DIRTY_BIT_DRAW_BUFFERS:
case gl::Framebuffer::DIRTY_BIT_READ_BUFFER:
break;
default:
{
ASSERT(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0 &&
dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX);
size_t colorIndex =
static_cast<size_t>(dirtyBit - gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0);
updateColorRenderTarget(colorIndex);
break;
}
}
}
// We should not have dirtied any additional state during our sync.
ASSERT(!mInternalDirtyBits.any());
FramebufferD3D::syncState(dirtyBits);
}
void Framebuffer11::markColorRenderTargetDirty(size_t colorIndex)
{
mInternalDirtyBits.set(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 + colorIndex);
mCachedColorRenderTargets[colorIndex] = nullptr;
}
void Framebuffer11::markDepthStencilRenderTargetDirty()
{
// Stencil is redundant in this case.
mInternalDirtyBits.set(gl::Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT);
mCachedDepthStencilRenderTarget = nullptr;
}
bool Framebuffer11::hasAnyInternalDirtyBit() const
{
return mInternalDirtyBits.any();
} }
void Framebuffer11::syncInternalState() const
{
// TODO(jmadill): Clean up this hack.
const_cast<Framebuffer11 *>(this)->syncState(gl::Framebuffer::DirtyBits());
}
} // namespace rx
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#define LIBANGLE_RENDERER_D3D_D3D11_FRAMBUFFER11_H_ #define LIBANGLE_RENDERER_D3D_D3D11_FRAMBUFFER11_H_
#include "libANGLE/renderer/d3d/FramebufferD3D.h" #include "libANGLE/renderer/d3d/FramebufferD3D.h"
#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
namespace rx namespace rx
{ {
...@@ -28,6 +29,24 @@ class Framebuffer11 : public FramebufferD3D ...@@ -28,6 +29,24 @@ class Framebuffer11 : public FramebufferD3D
// Invalidate the cached swizzles of all bound texture attachments. // Invalidate the cached swizzles of all bound texture attachments.
gl::Error invalidateSwizzles() const; gl::Error invalidateSwizzles() const;
void syncState(const gl::Framebuffer::DirtyBits &dirtyBits) override;
const RenderTargetArray &getCachedColorRenderTargets() const
{
return mCachedColorRenderTargets;
}
const RenderTarget11 *getCachedDepthStencilRenderTarget() const
{
return mCachedDepthStencilRenderTarget;
}
void markColorRenderTargetDirty(size_t colorIndex);
void markDepthStencilRenderTargetDirty();
bool hasAnyInternalDirtyBit() const;
// TODO(jmadill): make this non-const
void syncInternalState() const;
private: private:
gl::Error clear(const gl::Data &data, const ClearParameters &clearParams) override; gl::Error clear(const gl::Data &data, const ClearParameters &clearParams) override;
...@@ -46,7 +65,17 @@ class Framebuffer11 : public FramebufferD3D ...@@ -46,7 +65,17 @@ class Framebuffer11 : public FramebufferD3D
GLenum getRenderTargetImplementationFormat(RenderTargetD3D *renderTarget) const override; GLenum getRenderTargetImplementationFormat(RenderTargetD3D *renderTarget) const override;
void updateColorRenderTarget(size_t colorIndex);
void updateDepthStencilRenderTarget();
Renderer11 *const mRenderer; Renderer11 *const mRenderer;
RenderTargetArray mCachedColorRenderTargets;
RenderTarget11 *mCachedDepthStencilRenderTarget;
std::vector<NotificationCallback> mColorRenderTargetsDirty;
NotificationCallback mDepthStencilRenderTargetDirty;
gl::Framebuffer::DirtyBits mInternalDirtyBits;
}; };
} }
......
...@@ -178,6 +178,39 @@ static unsigned int getDSVSubresourceIndex(ID3D11Resource *resource, ID3D11Depth ...@@ -178,6 +178,39 @@ static unsigned int getDSVSubresourceIndex(ID3D11Resource *resource, ID3D11Depth
return D3D11CalcSubresource(mipSlice, arraySlice, mipLevels); return D3D11CalcSubresource(mipSlice, arraySlice, mipLevels);
} }
RenderTarget11::RenderTarget11()
{
}
RenderTarget11::~RenderTarget11()
{
signalDirty();
}
void RenderTarget11::addDirtyCallback(const NotificationCallback *callback)
{
mDirtyCallbacks.insert(callback);
}
void RenderTarget11::removeDirtyCallback(const NotificationCallback *callback)
{
mDirtyCallbacks.erase(callback);
}
void RenderTarget11::signalDirty()
{
if (mDirtyCallbacks.empty())
return;
for (const auto &callback : mDirtyCallbacks)
{
(*callback)();
}
// Clear the signal list. We can't do this in the callback because it mutates the iterator.
mDirtyCallbacks.clear();
}
TextureRenderTarget11::TextureRenderTarget11(ID3D11RenderTargetView *rtv, ID3D11Resource *resource, ID3D11ShaderResourceView *srv, TextureRenderTarget11::TextureRenderTarget11(ID3D11RenderTargetView *rtv, ID3D11Resource *resource, ID3D11ShaderResourceView *srv,
GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei samples) GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei samples)
: mWidth(width), : mWidth(width),
......
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
#include "libANGLE/renderer/d3d/RenderTargetD3D.h" #include "libANGLE/renderer/d3d/RenderTargetD3D.h"
#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
namespace rx namespace rx
{ {
class SwapChain11; class SwapChain11;
...@@ -20,8 +22,8 @@ class Renderer11; ...@@ -20,8 +22,8 @@ class Renderer11;
class RenderTarget11 : public RenderTargetD3D class RenderTarget11 : public RenderTargetD3D
{ {
public: public:
RenderTarget11() { } RenderTarget11();
virtual ~RenderTarget11() { } virtual ~RenderTarget11();
virtual ID3D11Resource *getTexture() const = 0; virtual ID3D11Resource *getTexture() const = 0;
virtual ID3D11RenderTargetView *getRenderTargetView() const = 0; virtual ID3D11RenderTargetView *getRenderTargetView() const = 0;
...@@ -31,6 +33,13 @@ class RenderTarget11 : public RenderTargetD3D ...@@ -31,6 +33,13 @@ class RenderTarget11 : public RenderTargetD3D
virtual unsigned int getSubresourceIndex() const = 0; virtual unsigned int getSubresourceIndex() const = 0;
virtual DXGI_FORMAT getDXGIFormat() const = 0; virtual DXGI_FORMAT getDXGIFormat() const = 0;
void addDirtyCallback(const NotificationCallback *callback);
void removeDirtyCallback(const NotificationCallback *callback);
void signalDirty() override;
protected:
std::set<const NotificationCallback *> mDirtyCallbacks;
}; };
class TextureRenderTarget11 : public RenderTarget11 class TextureRenderTarget11 : public RenderTarget11
......
...@@ -81,8 +81,6 @@ class StateManager11 final : angle::NonCopyable ...@@ -81,8 +81,6 @@ class StateManager11 final : angle::NonCopyable
void invalidateRenderTarget(); void invalidateRenderTarget();
void invalidateBoundViews(); void invalidateBoundViews();
void invalidateEverything(); void invalidateEverything();
bool setRenderTargets(const RenderTargetArray &renderTargets,
ID3D11DepthStencilView *depthStencil);
void setRenderTarget(ID3D11RenderTargetView *renderTarget, void setRenderTarget(ID3D11RenderTargetView *renderTarget,
ID3D11DepthStencilView *depthStencil); ID3D11DepthStencilView *depthStencil);
...@@ -91,10 +89,12 @@ class StateManager11 final : angle::NonCopyable ...@@ -91,10 +89,12 @@ class StateManager11 final : angle::NonCopyable
gl::Error onMakeCurrent(const gl::Data &data); gl::Error onMakeCurrent(const gl::Data &data);
private: private:
void setViewportBounds(const int width, const int height);
void unsetConflictingSRVs(gl::SamplerType shaderType, void unsetConflictingSRVs(gl::SamplerType shaderType,
uintptr_t resource, uintptr_t resource,
const gl::ImageIndex &index); const gl::ImageIndex &index);
void setViewportBounds(const int width, const int height); void unsetConflictingAttachmentResources(const gl::FramebufferAttachment *attachment,
ID3D11Resource *resource);
Renderer11 *mRenderer; Renderer11 *mRenderer;
...@@ -142,8 +142,7 @@ class StateManager11 final : angle::NonCopyable ...@@ -142,8 +142,7 @@ class StateManager11 final : angle::NonCopyable
int mCurPresentPathFastColorBufferHeight; int mCurPresentPathFastColorBufferHeight;
// Current RenderTarget state // Current RenderTarget state
std::array<uintptr_t, gl::IMPLEMENTATION_MAX_DRAW_BUFFERS> mAppliedRTVs; bool mRenderTargetIsDirty;
uintptr_t mAppliedDSV;
// Queries that are currently active in this state // Queries that are currently active in this state
std::set<Query11 *> mCurrentQueries; std::set<Query11 *> mCurrentQueries;
......
...@@ -960,6 +960,8 @@ gl::Error TextureStorage11_2D::copyToStorage(TextureStorage *destStorage) ...@@ -960,6 +960,8 @@ gl::Error TextureStorage11_2D::copyToStorage(TextureStorage *destStorage)
gl::Error TextureStorage11_2D::useLevelZeroWorkaroundTexture(bool useLevelZeroTexture) gl::Error TextureStorage11_2D::useLevelZeroWorkaroundTexture(bool useLevelZeroTexture)
{ {
bool lastSetting = mUseLevelZeroTexture;
if (useLevelZeroTexture && mMipLevels > 1) if (useLevelZeroTexture && mMipLevels > 1)
{ {
if (!mUseLevelZeroTexture && mTexture) if (!mUseLevelZeroTexture && mTexture)
...@@ -997,6 +999,22 @@ gl::Error TextureStorage11_2D::useLevelZeroWorkaroundTexture(bool useLevelZeroTe ...@@ -997,6 +999,22 @@ gl::Error TextureStorage11_2D::useLevelZeroWorkaroundTexture(bool useLevelZeroTe
mUseLevelZeroTexture = false; mUseLevelZeroTexture = false;
} }
if (lastSetting != mUseLevelZeroTexture)
{
// Mark everything as dirty to be conservative.
if (mLevelZeroRenderTarget)
{
mLevelZeroRenderTarget->signalDirty();
}
for (auto *renderTarget : mRenderTarget)
{
if (renderTarget)
{
renderTarget->signalDirty();
}
}
}
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#define LIBANGLE_RENDERER_D3D_D3D11_RENDERER11_UTILS_H_ #define LIBANGLE_RENDERER_D3D_D3D11_RENDERER11_UTILS_H_
#include <array> #include <array>
#include <functional>
#include <vector> #include <vector>
#include "libANGLE/angletypes.h" #include "libANGLE/angletypes.h"
...@@ -30,7 +31,8 @@ class RenderTarget11; ...@@ -30,7 +31,8 @@ class RenderTarget11;
struct WorkaroundsD3D; struct WorkaroundsD3D;
struct Renderer11DeviceCaps; struct Renderer11DeviceCaps;
using RenderTargetArray = std::array<ID3D11RenderTargetView *, gl::IMPLEMENTATION_MAX_DRAW_BUFFERS>; using RenderTargetArray = std::array<RenderTarget11 *, gl::IMPLEMENTATION_MAX_DRAW_BUFFERS>;
using RTVArray = std::array<ID3D11RenderTargetView *, gl::IMPLEMENTATION_MAX_DRAW_BUFFERS>;
namespace gl_d3d11 namespace gl_d3d11
{ {
...@@ -395,6 +397,8 @@ gl::ErrorOrResult<TextureHelper11> CreateStagingTexture(GLenum textureType, ...@@ -395,6 +397,8 @@ gl::ErrorOrResult<TextureHelper11> CreateStagingTexture(GLenum textureType,
bool UsePresentPathFast(const Renderer11 *renderer, const gl::FramebufferAttachment *colorbuffer); bool UsePresentPathFast(const Renderer11 *renderer, const gl::FramebufferAttachment *colorbuffer);
using NotificationCallback = std::function<void()>;
} // namespace rx } // namespace rx
#endif // LIBANGLE_RENDERER_D3D_D3D11_RENDERER11_UTILS_H_ #endif // LIBANGLE_RENDERER_D3D_D3D11_RENDERER11_UTILS_H_
...@@ -30,8 +30,8 @@ ...@@ -30,8 +30,8 @@
'<(angle_path)/src/tests/gl_tests/DrawElementsTest.cpp', '<(angle_path)/src/tests/gl_tests/DrawElementsTest.cpp',
'<(angle_path)/src/tests/gl_tests/ETCTextureTest.cpp', '<(angle_path)/src/tests/gl_tests/ETCTextureTest.cpp',
'<(angle_path)/src/tests/gl_tests/FenceSyncTests.cpp', '<(angle_path)/src/tests/gl_tests/FenceSyncTests.cpp',
'<(angle_path)/src/tests/gl_tests/FramebufferFormatsTest.cpp',
'<(angle_path)/src/tests/gl_tests/FramebufferRenderMipmapTest.cpp', '<(angle_path)/src/tests/gl_tests/FramebufferRenderMipmapTest.cpp',
'<(angle_path)/src/tests/gl_tests/FramebufferTest.cpp',
'<(angle_path)/src/tests/gl_tests/GLSLTest.cpp', '<(angle_path)/src/tests/gl_tests/GLSLTest.cpp',
'<(angle_path)/src/tests/gl_tests/ImageTest.cpp', '<(angle_path)/src/tests/gl_tests/ImageTest.cpp',
'<(angle_path)/src/tests/gl_tests/IncompleteTextureTest.cpp', '<(angle_path)/src/tests/gl_tests/IncompleteTextureTest.cpp',
......
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// //
// Framebuffer tests:
// Various tests related for Frambuffers.
//
#include "test_utils/ANGLETest.h" #include "test_utils/ANGLETest.h"
...@@ -38,8 +41,13 @@ class FramebufferFormatsTest : public ANGLETest ...@@ -38,8 +41,13 @@ class FramebufferFormatsTest : public ANGLETest
} }
} }
void testBitCounts(GLuint fbo, GLint minRedBits, GLint minGreenBits, GLint minBlueBits, void testBitCounts(GLuint fbo,
GLint minAlphaBits, GLint minDepthBits, GLint minStencilBits) GLint minRedBits,
GLint minGreenBits,
GLint minBlueBits,
GLint minAlphaBits,
GLint minDepthBits,
GLint minStencilBits)
{ {
checkBitCount(fbo, GL_RED_BITS, minRedBits); checkBitCount(fbo, GL_RED_BITS, minRedBits);
checkBitCount(fbo, GL_GREEN_BITS, minGreenBits); checkBitCount(fbo, GL_GREEN_BITS, minGreenBits);
...@@ -49,7 +57,10 @@ class FramebufferFormatsTest : public ANGLETest ...@@ -49,7 +57,10 @@ class FramebufferFormatsTest : public ANGLETest
checkBitCount(fbo, GL_STENCIL_BITS, minStencilBits); checkBitCount(fbo, GL_STENCIL_BITS, minStencilBits);
} }
void testTextureFormat(GLenum internalFormat, GLint minRedBits, GLint minGreenBits, GLint minBlueBits, void testTextureFormat(GLenum internalFormat,
GLint minRedBits,
GLint minGreenBits,
GLint minBlueBits,
GLint minAlphaBits) GLint minAlphaBits)
{ {
glGenTextures(1, &mTexture); glGenTextures(1, &mTexture);
...@@ -61,7 +72,9 @@ class FramebufferFormatsTest : public ANGLETest ...@@ -61,7 +72,9 @@ class FramebufferFormatsTest : public ANGLETest
testBitCounts(mFramebuffer, minRedBits, minGreenBits, minBlueBits, minAlphaBits, 0, 0); testBitCounts(mFramebuffer, minRedBits, minGreenBits, minBlueBits, minAlphaBits, 0, 0);
} }
void testRenderbufferMultisampleFormat(int minESVersion, GLenum attachmentType, GLenum internalFormat) void testRenderbufferMultisampleFormat(int minESVersion,
GLenum attachmentType,
GLenum internalFormat)
{ {
// TODO(geofflang): Figure out why this is broken on Intel OpenGL // TODO(geofflang): Figure out why this is broken on Intel OpenGL
if (isIntel() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE) if (isIntel() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
...@@ -290,7 +303,8 @@ TEST_P(FramebufferFormatsTest, IncompleteCubeMap) ...@@ -290,7 +303,8 @@ TEST_P(FramebufferFormatsTest, IncompleteCubeMap)
ASSERT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION); ASSERT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
} }
// 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_INSTANTIATE_TEST(FramebufferFormatsTest, ANGLE_INSTANTIATE_TEST(FramebufferFormatsTest,
ES2_D3D9(), ES2_D3D9(),
ES2_D3D11(), ES2_D3D11(),
...@@ -299,3 +313,45 @@ ANGLE_INSTANTIATE_TEST(FramebufferFormatsTest, ...@@ -299,3 +313,45 @@ ANGLE_INSTANTIATE_TEST(FramebufferFormatsTest,
ES3_OPENGL(), ES3_OPENGL(),
ES2_OPENGLES(), ES2_OPENGLES(),
ES3_OPENGLES()); ES3_OPENGLES());
class FramebufferInvalidateTest : public ANGLETest
{
protected:
FramebufferInvalidateTest() : mFramebuffer(0), mRenderbuffer(0) {}
void SetUp() override
{
ANGLETest::SetUp();
glGenFramebuffers(1, &mFramebuffer);
glGenRenderbuffers(1, &mRenderbuffer);
}
void TearDown() override
{
glDeleteFramebuffers(1, &mFramebuffer);
glDeleteRenderbuffers(1, &mRenderbuffer);
ANGLETest::TearDown();
}
GLuint mFramebuffer;
GLuint mRenderbuffer;
};
// Covers invalidating an incomplete framebuffer. This should be a no-op, but should not error.
TEST_P(FramebufferInvalidateTest, Incomplete)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mRenderbuffer);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
std::vector<GLenum> attachments;
attachments.push_back(GL_COLOR_ATTACHMENT0);
glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, attachments.data());
EXPECT_GL_NO_ERROR();
}
ANGLE_INSTANTIATE_TEST(FramebufferInvalidateTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
...@@ -1252,6 +1252,75 @@ TEST_P(ImageTest, Respecification) ...@@ -1252,6 +1252,75 @@ TEST_P(ImageTest, Respecification)
glDeleteTextures(1, &target); glDeleteTextures(1, &target);
} }
// First render to a target texture, then respecify the source texture, orphaning it.
// The target texture's FBO should be notified of the target texture's orphaning.
TEST_P(ImageTest, RespecificationWithFBO)
{
EGLWindow *window = getEGLWindow();
if (!extensionEnabled("OES_EGL_image") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_image_base") ||
!eglDisplayExtensionEnabled(window->getDisplay(), "EGL_KHR_gl_texture_2D_image"))
{
std::cout << "Test skipped because OES_EGL_image, EGL_KHR_image_base or "
"EGL_KHR_gl_texture_2D_image is not available."
<< std::endl;
return;
}
// Simple shader
const std::string &vertexSource =
"attribute vec2 position;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(position, 0, 1);\n"
"}";
const std::string &fragmentSource =
"void main()\n"
"{\n"
" gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);\n"
"}";
GLuint program = CompileProgram(vertexSource, fragmentSource);
ASSERT_NE(0u, program);
GLubyte originalData[4] = {255, 0, 255, 255};
GLubyte updateData[4] = {0, 255, 0, 255};
// Create the Image
GLuint source;
EGLImageKHR image;
createEGLImage2DTextureSource(1, 1, GL_RGBA, GL_UNSIGNED_BYTE, originalData, &source, &image);
// Create the target
GLuint target;
createEGLImageTargetTexture2D(image, &target);
// Render to the target texture
GLuint fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target, 0);
drawQuad(program, "position", 0.5f);
EXPECT_PIXEL_EQ(0, 0, 0, 0, 255, 255);
// Respecify source with same parameters. This should not change the texture storage in D3D11.
glBindTexture(GL_TEXTURE_2D, source);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, updateData);
// Expect that the source texture has the updated data
verifyResults2D(source, updateData);
// Render to the target texture again and verify it gets the rendered pixels.
drawQuad(program, "position", 0.5f);
EXPECT_PIXEL_EQ(0, 0, 0, 0, 255, 255);
// Clean up
glDeleteTextures(1, &source);
eglDestroyImageKHR(window->getDisplay(), image);
glDeleteTextures(1, &target);
glDeleteProgram(program);
glDeleteFramebuffers(1, &fbo);
}
// Test that respecifying a level of the target texture orphans it and keeps a copy of the EGLimage // Test that respecifying a level of the target texture orphans it and keeps a copy of the EGLimage
// data // data
TEST_P(ImageTest, RespecificationOfOtherLevel) TEST_P(ImageTest, RespecificationOfOtherLevel)
......
...@@ -240,5 +240,148 @@ TEST_P(StateChangeTestES3, ReadBufferAndDrawBuffersSync) ...@@ -240,5 +240,148 @@ TEST_P(StateChangeTestES3, ReadBufferAndDrawBuffersSync)
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
} }
class StateChangeRenderTest : public StateChangeTest
{
protected:
StateChangeRenderTest() : mProgram(0), mRenderbuffer(0) {}
void SetUp() override
{
StateChangeTest::SetUp();
const std::string vertexShaderSource =
"attribute vec2 position;\n"
"void main() {\n"
" gl_Position = vec4(position, 0, 1);\n"
"}";
const std::string fragmentShaderSource =
"uniform highp vec4 uniformColor;\n"
"void main() {\n"
" gl_FragColor = uniformColor;\n"
"}";
mProgram = CompileProgram(vertexShaderSource, fragmentShaderSource);
ASSERT_NE(0u, mProgram);
glGenRenderbuffers(1, &mRenderbuffer);
}
void TearDown() override
{
glDeleteProgram(mProgram);
glDeleteRenderbuffers(1, &mRenderbuffer);
StateChangeTest::TearDown();
}
void setUniformColor(const GLColor &color)
{
glUseProgram(mProgram);
const Vector4 &normalizedColor = color.toNormalizedVector();
GLint uniformLocation = glGetUniformLocation(mProgram, "uniformColor");
ASSERT_NE(-1, uniformLocation);
glUniform4fv(uniformLocation, 1, normalizedColor.data());
}
GLuint mProgram;
GLuint mRenderbuffer;
};
// Test that re-creating a currently attached texture works as expected.
TEST_P(StateChangeRenderTest, RecreateTexture)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glBindTexture(GL_TEXTURE_2D, mTextures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
// Draw with red to the FBO.
GLColor red(255, 0, 0, 255);
setUniformColor(red);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, red);
// Recreate the texture with green.
GLColor green(0, 255, 0, 255);
std::vector<GLColor> greenPixels(32 * 32, green);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE,
greenPixels.data());
EXPECT_PIXEL_COLOR_EQ(0, 0, green);
// Verify drawing blue gives blue. This covers the FBO sync with D3D dirty bits.
GLColor blue(0, 0, 255, 255);
setUniformColor(blue);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, blue);
EXPECT_GL_NO_ERROR();
}
// Test that re-creating a currently attached renderbuffer works as expected.
TEST_P(StateChangeRenderTest, RecreateRenderbuffer)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 16, 16);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mRenderbuffer);
// Draw with red to the FBO.
GLColor red(255, 0, 0, 255);
setUniformColor(red);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, red);
// Recreate the renderbuffer and clear to green.
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 32, 32);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
GLColor green(0, 255, 0, 255);
EXPECT_PIXEL_COLOR_EQ(0, 0, green);
// Verify drawing blue gives blue. This covers the FBO sync with D3D dirty bits.
GLColor blue(0, 0, 255, 255);
setUniformColor(blue);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, blue);
EXPECT_GL_NO_ERROR();
}
// Test that recreating a texture with GenerateMipmaps signals the FBO is dirty.
TEST_P(StateChangeRenderTest, GenerateMipmap)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glBindTexture(GL_TEXTURE_2D, mTextures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
// Draw once to set the RenderTarget in D3D11
GLColor red(255, 0, 0, 255);
setUniformColor(red);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, red);
// This will trigger the texture to be re-created on FL9_3.
glGenerateMipmap(GL_TEXTURE_2D);
// Now ensure we don't have a stale render target.
GLColor blue(0, 0, 255, 255);
setUniformColor(blue);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, blue);
EXPECT_GL_NO_ERROR();
}
ANGLE_INSTANTIATE_TEST(StateChangeTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL()); ANGLE_INSTANTIATE_TEST(StateChangeTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL());
ANGLE_INSTANTIATE_TEST(StateChangeRenderTest,
ES2_D3D9(),
ES2_D3D11(),
ES2_OPENGL(),
ES2_D3D11_FL9_3());
ANGLE_INSTANTIATE_TEST(StateChangeTestES3, ES3_D3D11(), ES3_OPENGL()); ANGLE_INSTANTIATE_TEST(StateChangeTestES3, ES3_D3D11(), ES3_OPENGL());
...@@ -15,6 +15,14 @@ ...@@ -15,6 +15,14 @@
namespace angle namespace angle
{ {
namespace
{
float ColorNorm(GLubyte channelValue)
{
return static_cast<float>(channelValue) / 255.0f;
}
} // anonymous namespace
GLColor::GLColor() : R(0), G(0), B(0), A(0) GLColor::GLColor() : R(0), G(0), B(0), A(0)
{ {
} }
...@@ -28,6 +36,11 @@ GLColor::GLColor(GLuint colorValue) : R(0), G(0), B(0), A(0) ...@@ -28,6 +36,11 @@ GLColor::GLColor(GLuint colorValue) : R(0), G(0), B(0), A(0)
memcpy(&R, &colorValue, sizeof(GLuint)); memcpy(&R, &colorValue, sizeof(GLuint));
} }
Vector4 GLColor::toNormalizedVector() const
{
return Vector4(ColorNorm(R), ColorNorm(G), ColorNorm(B), ColorNorm(A));
}
GLColor ReadColor(GLint x, GLint y) GLColor ReadColor(GLint x, GLint y)
{ {
GLColor actual; GLColor actual;
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "angle_test_configs.h" #include "angle_test_configs.h"
#include "common/angleutils.h" #include "common/angleutils.h"
#include "shader_utils.h" #include "shader_utils.h"
#include "Vector.h"
#define EXPECT_GL_ERROR(err) EXPECT_EQ(static_cast<GLenum>(err), glGetError()) #define EXPECT_GL_ERROR(err) EXPECT_EQ(static_cast<GLenum>(err), glGetError())
#define EXPECT_GL_NO_ERROR() EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()) #define EXPECT_GL_NO_ERROR() EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError())
...@@ -47,6 +48,8 @@ struct GLColor ...@@ -47,6 +48,8 @@ struct GLColor
GLColor(GLubyte r, GLubyte g, GLubyte b, GLubyte a); GLColor(GLubyte r, GLubyte g, GLubyte b, GLubyte a);
GLColor(GLuint colorValue); GLColor(GLuint colorValue);
Vector4 toNormalizedVector() const;
GLubyte R, G, B, A; GLubyte R, G, B, A;
}; };
......
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