зеркало из https://github.com/AvaloniaUI/angle.git
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/327255 Reviewed-by: Geoff Lang <geofflang@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
This commit is contained in:
Родитель
d834e3dc0d
Коммит
1fbc59fe2e
|
@ -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.
|
||||
perftests_paths = [
|
||||
os.path.join('out', 'Release_x64'),
|
||||
os.path.join('out', 'Release'),
|
||||
os.path.join('build', 'Release_x64'),
|
||||
os.path.join('build', 'Release_Win32')
|
||||
|
|
|
@ -299,6 +299,11 @@ GLenum Framebuffer::getDrawBufferState(size_t drawBuffer) const
|
|||
return mData.mDrawBufferStates[drawBuffer];
|
||||
}
|
||||
|
||||
const std::vector<GLenum> &Framebuffer::getDrawBufferStates() const
|
||||
{
|
||||
return mData.getDrawBufferStates();
|
||||
}
|
||||
|
||||
void Framebuffer::setDrawBuffers(size_t count, const GLenum *buffers)
|
||||
{
|
||||
auto &drawStates = mData.mDrawBufferStates;
|
||||
|
|
|
@ -119,6 +119,7 @@ class Framebuffer final : public LabeledObject
|
|||
|
||||
size_t getDrawbufferStateCount() const;
|
||||
GLenum getDrawBufferState(size_t drawBuffer) const;
|
||||
const std::vector<GLenum> &getDrawBufferStates() const;
|
||||
void setDrawBuffers(size_t count, const GLenum *buffers);
|
||||
const FramebufferAttachment *getDrawBuffer(size_t drawBuffer) const;
|
||||
bool hasEnabledDrawBuffer() const;
|
||||
|
|
|
@ -123,6 +123,9 @@ gl::Error EGLImageD3D::copyToLocalRendertarget()
|
|||
return error;
|
||||
}
|
||||
|
||||
// This only currently applies do D3D11, where it invalidates FBOs with this Image attached.
|
||||
curRenderTarget->signalDirty();
|
||||
|
||||
// Clear the source image buffers
|
||||
mBuffer = nullptr;
|
||||
mAttachmentBuffer = nullptr;
|
||||
|
|
|
@ -33,6 +33,9 @@ class RenderTargetD3D : public FramebufferAttachmentRenderTarget
|
|||
virtual unsigned int getSerial() const;
|
||||
static unsigned int issueSerials(unsigned int count);
|
||||
|
||||
// Only currently applies to D3D11.
|
||||
virtual void signalDirty() {}
|
||||
|
||||
private:
|
||||
const unsigned int mSerial;
|
||||
static unsigned int mCurrentSerial;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "libANGLE/renderer/d3d/d3d11/Framebuffer11.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/BitSetIterator.h"
|
||||
#include "libANGLE/renderer/d3d/d3d11/Buffer11.h"
|
||||
#include "libANGLE/renderer/d3d/d3d11/Clear11.h"
|
||||
#include "libANGLE/renderer/d3d/d3d11/TextureStorage11.h"
|
||||
|
@ -24,17 +25,9 @@
|
|||
namespace rx
|
||||
{
|
||||
|
||||
Framebuffer11::Framebuffer11(const gl::Framebuffer::Data &data, Renderer11 *renderer)
|
||||
: FramebufferD3D(data, renderer), mRenderer(renderer)
|
||||
namespace
|
||||
{
|
||||
ASSERT(mRenderer != nullptr);
|
||||
}
|
||||
|
||||
Framebuffer11::~Framebuffer11()
|
||||
{
|
||||
}
|
||||
|
||||
static gl::Error InvalidateAttachmentSwizzles(const gl::FramebufferAttachment *attachment)
|
||||
gl::Error InvalidateAttachmentSwizzles(const gl::FramebufferAttachment *attachment)
|
||||
{
|
||||
if (attachment && attachment->type() == GL_TEXTURE)
|
||||
{
|
||||
|
@ -61,6 +54,67 @@ static gl::Error InvalidateAttachmentSwizzles(const gl::FramebufferAttachment *a
|
|||
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
|
||||
{
|
||||
for (const auto &colorAttachment : mData.getColorAttachments())
|
||||
|
@ -425,4 +479,77 @@ GLenum Framebuffer11::getRenderTargetImplementationFormat(RenderTargetD3D *rende
|
|||
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 @@
|
|||
#define LIBANGLE_RENDERER_D3D_D3D11_FRAMBUFFER11_H_
|
||||
|
||||
#include "libANGLE/renderer/d3d/FramebufferD3D.h"
|
||||
#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
|
||||
|
||||
namespace rx
|
||||
{
|
||||
|
@ -28,6 +29,24 @@ class Framebuffer11 : public FramebufferD3D
|
|||
// Invalidate the cached swizzles of all bound texture attachments.
|
||||
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:
|
||||
gl::Error clear(const gl::Data &data, const ClearParameters &clearParams) override;
|
||||
|
||||
|
@ -46,7 +65,17 @@ class Framebuffer11 : public FramebufferD3D
|
|||
|
||||
GLenum getRenderTargetImplementationFormat(RenderTargetD3D *renderTarget) const override;
|
||||
|
||||
void updateColorRenderTarget(size_t colorIndex);
|
||||
void updateDepthStencilRenderTarget();
|
||||
|
||||
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
|
|||
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,
|
||||
GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei samples)
|
||||
: mWidth(width),
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
#include "libANGLE/renderer/d3d/RenderTargetD3D.h"
|
||||
|
||||
#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
|
||||
|
||||
namespace rx
|
||||
{
|
||||
class SwapChain11;
|
||||
|
@ -20,8 +22,8 @@ class Renderer11;
|
|||
class RenderTarget11 : public RenderTargetD3D
|
||||
{
|
||||
public:
|
||||
RenderTarget11() { }
|
||||
virtual ~RenderTarget11() { }
|
||||
RenderTarget11();
|
||||
virtual ~RenderTarget11();
|
||||
|
||||
virtual ID3D11Resource *getTexture() const = 0;
|
||||
virtual ID3D11RenderTargetView *getRenderTargetView() const = 0;
|
||||
|
@ -31,6 +33,13 @@ class RenderTarget11 : public RenderTargetD3D
|
|||
virtual unsigned int getSubresourceIndex() 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
|
||||
|
|
|
@ -150,9 +150,7 @@ StateManager11::StateManager11(Renderer11 *renderer)
|
|||
mCurNear(0.0f),
|
||||
mCurFar(0.0f),
|
||||
mViewportBounds(),
|
||||
mCurPresentPathFastEnabled(false),
|
||||
mCurPresentPathFastColorBufferHeight(0),
|
||||
mAppliedDSV(angle::DirtyPointer)
|
||||
mRenderTargetIsDirty(false)
|
||||
{
|
||||
mCurBlendState.blend = false;
|
||||
mCurBlendState.sourceBlendRGB = GL_ONE;
|
||||
|
@ -459,6 +457,9 @@ void StateManager11::syncState(const gl::State &state, const gl::State::DirtyBit
|
|||
mViewportStateIsDirty = true;
|
||||
}
|
||||
break;
|
||||
case gl::State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING:
|
||||
mRenderTargetIsDirty = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -773,11 +774,7 @@ void StateManager11::setViewport(const gl::Caps *caps,
|
|||
|
||||
void StateManager11::invalidateRenderTarget()
|
||||
{
|
||||
for (auto &appliedRTV : mAppliedRTVs)
|
||||
{
|
||||
appliedRTV = angle::DirtyPointer;
|
||||
}
|
||||
mAppliedDSV = angle::DirtyPointer;
|
||||
mRenderTargetIsDirty = true;
|
||||
}
|
||||
|
||||
void StateManager11::invalidateBoundViews()
|
||||
|
@ -802,34 +799,6 @@ void StateManager11::invalidateEverything()
|
|||
invalidateBoundViews();
|
||||
}
|
||||
|
||||
bool StateManager11::setRenderTargets(const RenderTargetArray &renderTargets,
|
||||
ID3D11DepthStencilView *depthStencil)
|
||||
{
|
||||
// TODO(jmadill): Use context caps?
|
||||
UINT drawBuffers = mRenderer->getRendererCaps().maxDrawBuffers;
|
||||
|
||||
// Apply the render target and depth stencil
|
||||
size_t arraySize = sizeof(uintptr_t) * drawBuffers;
|
||||
if (memcmp(renderTargets.data(), mAppliedRTVs.data(), arraySize) == 0 &&
|
||||
reinterpret_cast<uintptr_t>(depthStencil) == mAppliedDSV)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// The D3D11 blend state is heavily dependent on the current render target.
|
||||
mBlendStateIsDirty = true;
|
||||
|
||||
for (UINT rtIndex = 0; rtIndex < drawBuffers; rtIndex++)
|
||||
{
|
||||
mAppliedRTVs[rtIndex] = reinterpret_cast<uintptr_t>(renderTargets[rtIndex]);
|
||||
}
|
||||
mAppliedDSV = reinterpret_cast<uintptr_t>(depthStencil);
|
||||
|
||||
mRenderer->getDeviceContext()->OMSetRenderTargets(drawBuffers, renderTargets.data(),
|
||||
depthStencil);
|
||||
return true;
|
||||
}
|
||||
|
||||
void StateManager11::setRenderTarget(ID3D11RenderTargetView *renderTarget,
|
||||
ID3D11DepthStencilView *depthStencil)
|
||||
{
|
||||
|
@ -954,6 +923,22 @@ void StateManager11::unsetConflictingSRVs(gl::SamplerType samplerType,
|
|||
}
|
||||
}
|
||||
|
||||
void StateManager11::unsetConflictingAttachmentResources(
|
||||
const gl::FramebufferAttachment *attachment,
|
||||
ID3D11Resource *resource)
|
||||
{
|
||||
// Unbind render target SRVs from the shader here to prevent D3D11 warnings.
|
||||
if (attachment->type() == GL_TEXTURE)
|
||||
{
|
||||
uintptr_t resourcePtr = reinterpret_cast<uintptr_t>(resource);
|
||||
const gl::ImageIndex &index = attachment->getTextureImageIndex();
|
||||
// The index doesn't need to be corrected for the small compressed texture workaround
|
||||
// because a rendertarget is never compressed.
|
||||
unsetConflictingSRVs(gl::SAMPLER_VERTEX, resourcePtr, index);
|
||||
unsetConflictingSRVs(gl::SAMPLER_PIXEL, resourcePtr, index);
|
||||
}
|
||||
}
|
||||
|
||||
void StateManager11::initialize(const gl::Caps &caps)
|
||||
{
|
||||
mCurVertexSRVs.initialize(caps.maxVertexTextureImageUnits);
|
||||
|
@ -965,85 +950,90 @@ void StateManager11::initialize(const gl::Caps &caps)
|
|||
|
||||
gl::Error StateManager11::syncFramebuffer(const gl::Framebuffer *framebuffer)
|
||||
{
|
||||
const Framebuffer11 *framebuffer11 = GetImplAs<Framebuffer11>(framebuffer);
|
||||
gl::Error error = framebuffer11->invalidateSwizzles();
|
||||
if (error.isError())
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
if (framebuffer11->hasAnyInternalDirtyBit())
|
||||
{
|
||||
ASSERT(framebuffer->id() != 0);
|
||||
framebuffer11->syncInternalState();
|
||||
}
|
||||
|
||||
if (!mRenderTargetIsDirty)
|
||||
{
|
||||
return gl::Error(GL_NO_ERROR);
|
||||
}
|
||||
|
||||
mRenderTargetIsDirty = false;
|
||||
|
||||
// Check for zero-sized default framebuffer, which is a special case.
|
||||
// in this case we do not wish to modify any state and just silently return false.
|
||||
// this will not report any gl error but will cause the calling method to return.
|
||||
if (framebuffer->id() == 0)
|
||||
{
|
||||
ASSERT(!framebuffer11->hasAnyInternalDirtyBit());
|
||||
const gl::Extents &size = framebuffer->getFirstColorbuffer()->getSize();
|
||||
if (size.width == 0 || size.height == 0)
|
||||
{
|
||||
return gl::Error(GL_NO_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the color render buffer and serial
|
||||
// Also extract the render target dimensions and view
|
||||
unsigned int renderTargetWidth = 0;
|
||||
unsigned int renderTargetHeight = 0;
|
||||
DXGI_FORMAT renderTargetFormat = DXGI_FORMAT_UNKNOWN;
|
||||
RenderTargetArray framebufferRTVs;
|
||||
RTVArray framebufferRTVs;
|
||||
bool missingColorRenderTarget = true;
|
||||
|
||||
framebufferRTVs.fill(nullptr);
|
||||
|
||||
const Framebuffer11 *framebuffer11 = GetImplAs<Framebuffer11>(framebuffer);
|
||||
const gl::AttachmentList &colorbuffers = framebuffer11->getColorAttachmentsForRender();
|
||||
const auto &colorRTs = framebuffer11->getCachedColorRenderTargets();
|
||||
|
||||
for (size_t colorAttachment = 0; colorAttachment < colorbuffers.size(); ++colorAttachment)
|
||||
size_t appliedRTIndex = 0;
|
||||
bool skipInactiveRTs = mRenderer->getWorkarounds().mrtPerfWorkaround;
|
||||
const auto &drawStates = framebuffer->getDrawBufferStates();
|
||||
|
||||
for (size_t rtIndex = 0; rtIndex < colorRTs.size(); ++rtIndex)
|
||||
{
|
||||
const gl::FramebufferAttachment *colorbuffer = colorbuffers[colorAttachment];
|
||||
const RenderTarget11 *renderTarget = colorRTs[rtIndex];
|
||||
|
||||
if (colorbuffer)
|
||||
// Skip inactive rendertargets if the workaround is enabled.
|
||||
if (skipInactiveRTs && (!renderTarget || drawStates[rtIndex] == GL_NONE))
|
||||
{
|
||||
// the draw buffer must be either "none", "back" for the default buffer or the same
|
||||
// index as this color (in order)
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for zero-sized default framebuffer, which is a special case.
|
||||
// in this case we do not wish to modify any state and just silently return false.
|
||||
// this will not report any gl error but will cause the calling method to return.
|
||||
const gl::Extents &size = colorbuffer->getSize();
|
||||
if (size.width == 0 || size.height == 0)
|
||||
{
|
||||
return gl::Error(GL_NO_ERROR);
|
||||
}
|
||||
|
||||
// Extract the render target dimensions and view
|
||||
RenderTarget11 *renderTarget = NULL;
|
||||
gl::Error error = colorbuffer->getRenderTarget(&renderTarget);
|
||||
if (error.isError())
|
||||
{
|
||||
return error;
|
||||
}
|
||||
ASSERT(renderTarget);
|
||||
|
||||
framebufferRTVs[colorAttachment] = renderTarget->getRenderTargetView();
|
||||
ASSERT(framebufferRTVs[colorAttachment]);
|
||||
if (renderTarget)
|
||||
{
|
||||
framebufferRTVs[appliedRTIndex] = renderTarget->getRenderTargetView();
|
||||
ASSERT(framebufferRTVs[appliedRTIndex]);
|
||||
|
||||
if (missingColorRenderTarget)
|
||||
{
|
||||
renderTargetWidth = renderTarget->getWidth();
|
||||
renderTargetHeight = renderTarget->getHeight();
|
||||
renderTargetFormat = renderTarget->getDXGIFormat();
|
||||
missingColorRenderTarget = false;
|
||||
}
|
||||
|
||||
// Unbind render target SRVs from the shader here to prevent D3D11 warnings.
|
||||
if (colorbuffer->type() == GL_TEXTURE)
|
||||
{
|
||||
uintptr_t rtResource =
|
||||
reinterpret_cast<uintptr_t>(GetViewResource(framebufferRTVs[colorAttachment]));
|
||||
const gl::ImageIndex &index = colorbuffer->getTextureImageIndex();
|
||||
// The index doesn't need to be corrected for the small compressed texture
|
||||
// workaround
|
||||
// because a rendertarget is never compressed.
|
||||
unsetConflictingSRVs(gl::SAMPLER_VERTEX, rtResource, index);
|
||||
unsetConflictingSRVs(gl::SAMPLER_PIXEL, rtResource, index);
|
||||
}
|
||||
}
|
||||
|
||||
// Unset conflicting texture SRVs
|
||||
const auto *attachment = framebuffer->getColorbuffer(rtIndex);
|
||||
ASSERT(attachment);
|
||||
unsetConflictingAttachmentResources(attachment, renderTarget->getTexture());
|
||||
|
||||
appliedRTIndex++;
|
||||
}
|
||||
|
||||
// Get the depth stencil buffers
|
||||
ID3D11DepthStencilView *framebufferDSV = NULL;
|
||||
const gl::FramebufferAttachment *depthStencil = framebuffer->getDepthOrStencilbuffer();
|
||||
if (depthStencil)
|
||||
ID3D11DepthStencilView *framebufferDSV = nullptr;
|
||||
const auto *depthStencilRenderTarget = framebuffer11->getCachedDepthStencilRenderTarget();
|
||||
if (depthStencilRenderTarget)
|
||||
{
|
||||
RenderTarget11 *depthStencilRenderTarget = NULL;
|
||||
gl::Error error = depthStencil->getRenderTarget(&depthStencilRenderTarget);
|
||||
if (error.isError())
|
||||
{
|
||||
return error;
|
||||
}
|
||||
ASSERT(depthStencilRenderTarget);
|
||||
|
||||
framebufferDSV = depthStencilRenderTarget->getDepthStencilView();
|
||||
ASSERT(framebufferDSV);
|
||||
|
||||
|
@ -1055,29 +1045,23 @@ gl::Error StateManager11::syncFramebuffer(const gl::Framebuffer *framebuffer)
|
|||
renderTargetHeight = depthStencilRenderTarget->getHeight();
|
||||
}
|
||||
|
||||
// Unbind render target SRVs from the shader here to prevent D3D11 warnings.
|
||||
if (depthStencil->type() == GL_TEXTURE)
|
||||
{
|
||||
uintptr_t depthStencilResource =
|
||||
reinterpret_cast<uintptr_t>(GetViewResource(framebufferDSV));
|
||||
const gl::ImageIndex &index = depthStencil->getTextureImageIndex();
|
||||
// The index doesn't need to be corrected for the small compressed texture workaround
|
||||
// because a rendertarget is never compressed.
|
||||
unsetConflictingSRVs(gl::SAMPLER_VERTEX, depthStencilResource, index);
|
||||
unsetConflictingSRVs(gl::SAMPLER_PIXEL, depthStencilResource, index);
|
||||
}
|
||||
// Unset conflicting texture SRVs
|
||||
const auto *attachment = framebuffer->getDepthOrStencilbuffer();
|
||||
ASSERT(attachment);
|
||||
unsetConflictingAttachmentResources(attachment, depthStencilRenderTarget->getTexture());
|
||||
}
|
||||
|
||||
if (setRenderTargets(framebufferRTVs, framebufferDSV))
|
||||
{
|
||||
setViewportBounds(renderTargetWidth, renderTargetHeight);
|
||||
}
|
||||
// TODO(jmadill): Use context caps?
|
||||
UINT drawBuffers = mRenderer->getRendererCaps().maxDrawBuffers;
|
||||
|
||||
gl::Error error = framebuffer11->invalidateSwizzles();
|
||||
if (error.isError())
|
||||
{
|
||||
return error;
|
||||
}
|
||||
// Apply the render target and depth stencil
|
||||
mRenderer->getDeviceContext()->OMSetRenderTargets(drawBuffers, framebufferRTVs.data(),
|
||||
framebufferDSV);
|
||||
|
||||
// The D3D11 blend state is heavily dependent on the current render target.
|
||||
mBlendStateIsDirty = true;
|
||||
|
||||
setViewportBounds(renderTargetWidth, renderTargetHeight);
|
||||
|
||||
return gl::Error(GL_NO_ERROR);
|
||||
}
|
||||
|
|
|
@ -81,8 +81,6 @@ class StateManager11 final : angle::NonCopyable
|
|||
void invalidateRenderTarget();
|
||||
void invalidateBoundViews();
|
||||
void invalidateEverything();
|
||||
bool setRenderTargets(const RenderTargetArray &renderTargets,
|
||||
ID3D11DepthStencilView *depthStencil);
|
||||
void setRenderTarget(ID3D11RenderTargetView *renderTarget,
|
||||
ID3D11DepthStencilView *depthStencil);
|
||||
|
||||
|
@ -91,10 +89,12 @@ class StateManager11 final : angle::NonCopyable
|
|||
gl::Error onMakeCurrent(const gl::Data &data);
|
||||
|
||||
private:
|
||||
void setViewportBounds(const int width, const int height);
|
||||
void unsetConflictingSRVs(gl::SamplerType shaderType,
|
||||
uintptr_t resource,
|
||||
const gl::ImageIndex &index);
|
||||
void setViewportBounds(const int width, const int height);
|
||||
void unsetConflictingAttachmentResources(const gl::FramebufferAttachment *attachment,
|
||||
ID3D11Resource *resource);
|
||||
|
||||
Renderer11 *mRenderer;
|
||||
|
||||
|
@ -142,8 +142,7 @@ class StateManager11 final : angle::NonCopyable
|
|||
int mCurPresentPathFastColorBufferHeight;
|
||||
|
||||
// Current RenderTarget state
|
||||
std::array<uintptr_t, gl::IMPLEMENTATION_MAX_DRAW_BUFFERS> mAppliedRTVs;
|
||||
uintptr_t mAppliedDSV;
|
||||
bool mRenderTargetIsDirty;
|
||||
|
||||
// Queries that are currently active in this state
|
||||
std::set<Query11 *> mCurrentQueries;
|
||||
|
|
|
@ -960,6 +960,8 @@ gl::Error TextureStorage11_2D::copyToStorage(TextureStorage *destStorage)
|
|||
|
||||
gl::Error TextureStorage11_2D::useLevelZeroWorkaroundTexture(bool useLevelZeroTexture)
|
||||
{
|
||||
bool lastSetting = mUseLevelZeroTexture;
|
||||
|
||||
if (useLevelZeroTexture && mMipLevels > 1)
|
||||
{
|
||||
if (!mUseLevelZeroTexture && mTexture)
|
||||
|
@ -997,6 +999,22 @@ gl::Error TextureStorage11_2D::useLevelZeroWorkaroundTexture(bool useLevelZeroTe
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#define LIBANGLE_RENDERER_D3D_D3D11_RENDERER11_UTILS_H_
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "libANGLE/angletypes.h"
|
||||
|
@ -30,7 +31,8 @@ class RenderTarget11;
|
|||
struct WorkaroundsD3D;
|
||||
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
|
||||
{
|
||||
|
@ -395,6 +397,8 @@ gl::ErrorOrResult<TextureHelper11> CreateStagingTexture(GLenum textureType,
|
|||
|
||||
bool UsePresentPathFast(const Renderer11 *renderer, const gl::FramebufferAttachment *colorbuffer);
|
||||
|
||||
using NotificationCallback = std::function<void()>;
|
||||
|
||||
} // namespace rx
|
||||
|
||||
#endif // LIBANGLE_RENDERER_D3D_D3D11_RENDERER11_UTILS_H_
|
||||
|
|
|
@ -30,8 +30,8 @@
|
|||
'<(angle_path)/src/tests/gl_tests/DrawElementsTest.cpp',
|
||||
'<(angle_path)/src/tests/gl_tests/ETCTextureTest.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/FramebufferTest.cpp',
|
||||
'<(angle_path)/src/tests/gl_tests/GLSLTest.cpp',
|
||||
'<(angle_path)/src/tests/gl_tests/ImageTest.cpp',
|
||||
'<(angle_path)/src/tests/gl_tests/IncompleteTextureTest.cpp',
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// Framebuffer tests:
|
||||
// Various tests related for Frambuffers.
|
||||
//
|
||||
|
||||
#include "test_utils/ANGLETest.h"
|
||||
|
||||
|
@ -38,8 +41,13 @@ class FramebufferFormatsTest : public ANGLETest
|
|||
}
|
||||
}
|
||||
|
||||
void testBitCounts(GLuint fbo, GLint minRedBits, GLint minGreenBits, GLint minBlueBits,
|
||||
GLint minAlphaBits, GLint minDepthBits, GLint minStencilBits)
|
||||
void testBitCounts(GLuint fbo,
|
||||
GLint minRedBits,
|
||||
GLint minGreenBits,
|
||||
GLint minBlueBits,
|
||||
GLint minAlphaBits,
|
||||
GLint minDepthBits,
|
||||
GLint minStencilBits)
|
||||
{
|
||||
checkBitCount(fbo, GL_RED_BITS, minRedBits);
|
||||
checkBitCount(fbo, GL_GREEN_BITS, minGreenBits);
|
||||
|
@ -49,7 +57,10 @@ class FramebufferFormatsTest : public ANGLETest
|
|||
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)
|
||||
{
|
||||
glGenTextures(1, &mTexture);
|
||||
|
@ -61,7 +72,9 @@ class FramebufferFormatsTest : public ANGLETest
|
|||
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
|
||||
if (isIntel() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
|
||||
|
@ -290,7 +303,8 @@ TEST_P(FramebufferFormatsTest, IncompleteCubeMap)
|
|||
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,
|
||||
ES2_D3D9(),
|
||||
ES2_D3D11(),
|
||||
|
@ -299,3 +313,45 @@ ANGLE_INSTANTIATE_TEST(FramebufferFormatsTest,
|
|||
ES3_OPENGL(),
|
||||
ES2_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)
|
|||
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
|
||||
// data
|
||||
TEST_P(ImageTest, RespecificationOfOtherLevel)
|
||||
|
|
|
@ -240,5 +240,148 @@ TEST_P(StateChangeTestES3, ReadBufferAndDrawBuffersSync)
|
|||
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(StateChangeRenderTest,
|
||||
ES2_D3D9(),
|
||||
ES2_D3D11(),
|
||||
ES2_OPENGL(),
|
||||
ES2_D3D11_FL9_3());
|
||||
ANGLE_INSTANTIATE_TEST(StateChangeTestES3, ES3_D3D11(), ES3_OPENGL());
|
||||
|
|
|
@ -15,6 +15,14 @@
|
|||
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)
|
||||
{
|
||||
}
|
||||
|
@ -28,6 +36,11 @@ GLColor::GLColor(GLuint colorValue) : R(0), G(0), B(0), A(0)
|
|||
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 actual;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "angle_test_configs.h"
|
||||
#include "common/angleutils.h"
|
||||
#include "shader_utils.h"
|
||||
#include "Vector.h"
|
||||
|
||||
#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())
|
||||
|
@ -47,6 +48,8 @@ struct GLColor
|
|||
GLColor(GLubyte r, GLubyte g, GLubyte b, GLubyte a);
|
||||
GLColor(GLuint colorValue);
|
||||
|
||||
Vector4 toNormalizedVector() const;
|
||||
|
||||
GLubyte R, G, B, A;
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче