Commit d444255a by Jamie Madill Committed by Commit Bot

Refactor signal utils into Observer pattern.

These types were over-generalized. All use cases featured arrays of resources attached to single parent resources. The channel ID is sufficient to identify the child resource in the parent, and having variadic template arguments wasn't necessary. Futhermore we can rename these types to use the common Observer pattern. This should make them more readable to new developers. Also update some classes to inherit from Subject instead of having a member Subject. This cleans up the code in a few places. This should lead to a simpler refactor to allow dependent dirty bits notifications in the Vulkan back-end. In the following patch the signal_utils files will be renamed. They are not renamed in this patch to ensure git history is preserved. Bug: angleproject:2372 Change-Id: I17a3f2c8d92afd4bb3cba2d378c3a2e8a6d7fb11 Reviewed-on: https://chromium-review.googlesource.com/936690Reviewed-by: 's avatarLuc Ferron <lucferron@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 2f3a0dc5
......@@ -2625,10 +2625,10 @@ void Context::requestExtension(const char *name)
// Invalidate all textures and framebuffer. Some extensions make new formats renderable or
// sampleable.
mState.mTextures->signalAllTexturesDirty();
mState.mTextures->signalAllTexturesDirty(this);
for (auto &zeroTexture : mZeroTextures)
{
zeroTexture.second->signalDirty(InitState::Initialized);
zeroTexture.second->signalDirty(this, InitState::Initialized);
}
mState.mFramebuffers->invalidateFramebufferComplenessCache();
......
......@@ -133,7 +133,7 @@ class FramebufferState final : angle::NonCopyable
angle::BitSet<IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS + 2> mResourceNeedsInit;
};
class Framebuffer final : public LabeledObject, public OnAttachmentDirtyReceiver
class Framebuffer final : public LabeledObject, public angle::ObserverInterface
{
public:
// Constructor to build application-defined framebuffers
......@@ -250,22 +250,22 @@ class Framebuffer final : public LabeledObject, public OnAttachmentDirtyReceiver
Error invalidateSub(const Context *context,
size_t count,
const GLenum *attachments,
const gl::Rectangle &area);
const Rectangle &area);
Error clear(const gl::Context *context, GLbitfield mask);
Error clearBufferfv(const gl::Context *context,
Error clear(const Context *context, GLbitfield mask);
Error clearBufferfv(const Context *context,
GLenum buffer,
GLint drawbuffer,
const GLfloat *values);
Error clearBufferuiv(const gl::Context *context,
Error clearBufferuiv(const Context *context,
GLenum buffer,
GLint drawbuffer,
const GLuint *values);
Error clearBufferiv(const gl::Context *context,
Error clearBufferiv(const Context *context,
GLenum buffer,
GLint drawbuffer,
const GLint *values);
Error clearBufferfi(const gl::Context *context,
Error clearBufferfi(const Context *context,
GLenum buffer,
GLint drawbuffer,
GLfloat depth,
......@@ -273,13 +273,13 @@ class Framebuffer final : public LabeledObject, public OnAttachmentDirtyReceiver
GLenum getImplementationColorReadFormat(const Context *context) const;
GLenum getImplementationColorReadType(const Context *context) const;
Error readPixels(const gl::Context *context,
const gl::Rectangle &area,
Error readPixels(const Context *context,
const Rectangle &area,
GLenum format,
GLenum type,
void *pixels);
Error blit(const gl::Context *context,
Error blit(const Context *context,
const Rectangle &sourceArea,
const Rectangle &destArea,
GLbitfield mask,
......@@ -289,7 +289,7 @@ class Framebuffer final : public LabeledObject, public OnAttachmentDirtyReceiver
{
DIRTY_BIT_COLOR_ATTACHMENT_0,
DIRTY_BIT_COLOR_ATTACHMENT_MAX =
DIRTY_BIT_COLOR_ATTACHMENT_0 + gl::IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS,
DIRTY_BIT_COLOR_ATTACHMENT_0 + IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS,
DIRTY_BIT_DEPTH_ATTACHMENT = DIRTY_BIT_COLOR_ATTACHMENT_MAX,
DIRTY_BIT_STENCIL_ATTACHMENT,
DIRTY_BIT_DRAW_BUFFERS,
......@@ -307,8 +307,10 @@ class Framebuffer final : public LabeledObject, public OnAttachmentDirtyReceiver
void syncState(const Context *context);
// OnAttachmentChangedReceiver implementation
void signal(size_t dirtyBit, InitState state) override;
// Observer implementation
void onSubjectStateChange(const Context *context,
angle::SubjectIndex index,
angle::SubjectMessage message) override;
bool formsRenderingFeedbackLoopWith(const State &state) const;
bool formsCopyingFeedbackLoopWith(GLuint copyTextureID,
......@@ -355,7 +357,7 @@ class Framebuffer final : public LabeledObject, public OnAttachmentDirtyReceiver
void updateAttachment(const Context *context,
FramebufferAttachment *attachment,
size_t dirtyBit,
OnAttachmentDirtyBinding *onDirtyBinding,
angle::ObserverBinding *onDirtyBinding,
GLenum type,
GLenum binding,
const ImageIndex &textureIndex,
......@@ -376,14 +378,16 @@ class Framebuffer final : public LabeledObject, public OnAttachmentDirtyReceiver
bool partialClearNeedsInit(const Context *context, bool color, bool depth, bool stencil);
bool partialBufferClearNeedsInit(const Context *context, GLenum bufferType);
FramebufferAttachment *getAttachmentFromSubjectIndex(angle::SubjectIndex index);
FramebufferState mState;
rx::FramebufferImpl *mImpl;
GLuint mId;
Optional<GLenum> mCachedStatus;
std::vector<OnAttachmentDirtyBinding> mDirtyColorAttachmentBindings;
OnAttachmentDirtyBinding mDirtyDepthAttachmentBinding;
OnAttachmentDirtyBinding mDirtyStencilAttachmentBinding;
std::vector<angle::ObserverBinding> mDirtyColorAttachmentBindings;
angle::ObserverBinding mDirtyDepthAttachmentBinding;
angle::ObserverBinding mDirtyStencilAttachmentBinding;
DirtyBits mDirtyBits;
......
......@@ -353,11 +353,6 @@ Error FramebufferAttachmentObject::getAttachmentRenderTarget(
return getAttachmentImpl()->getAttachmentRenderTarget(context, binding, imageIndex, rtOut);
}
OnAttachmentDirtyChannel *FramebufferAttachmentObject::getDirtyChannel()
{
return &mDirtyChannel;
}
Error FramebufferAttachmentObject::initializeContents(const Context *context,
const ImageIndex &imageIndex)
{
......
......@@ -49,10 +49,6 @@ enum class InitState
Initialized,
};
using OnAttachmentDirtyBinding = angle::ChannelBinding<size_t, InitState>;
using OnAttachmentDirtyChannel = angle::BroadcastChannel<size_t, InitState>;
using OnAttachmentDirtyReceiver = angle::SignalReceiver<size_t, InitState>;
// FramebufferAttachment implements a GL framebuffer attachment.
// Attachments are "light" containers, which store pointers to ref-counted GL objects.
// We support GL texture (2D/3D/Cube/2D array) and renderbuffer object attachments.
......@@ -184,7 +180,7 @@ class FramebufferAttachment final
};
// A base class for objects that FBO Attachments may point to.
class FramebufferAttachmentObject
class FramebufferAttachmentObject : public angle::Subject
{
public:
FramebufferAttachmentObject();
......@@ -210,12 +206,8 @@ class FramebufferAttachmentObject
Error initializeContents(const Context *context, const ImageIndex &imageIndex);
OnAttachmentDirtyChannel *getDirtyChannel();
protected:
virtual rx::FramebufferAttachmentObjectImpl *getAttachmentImpl() const = 0;
OnAttachmentDirtyChannel mDirtyChannel;
};
inline Extents FramebufferAttachment::getSize() const
......
......@@ -109,7 +109,7 @@ Error Renderbuffer::setStorage(const Context *context,
mState.update(static_cast<GLsizei>(width), static_cast<GLsizei>(height), Format(internalformat),
0, InitState::MayNeedInit);
mDirtyChannel.signal(mState.mInitState);
onStateChange(context, angle::SubjectMessage::STATE_CHANGE);
return NoError();
}
......@@ -126,7 +126,7 @@ Error Renderbuffer::setStorageMultisample(const Context *context,
mState.update(static_cast<GLsizei>(width), static_cast<GLsizei>(height), Format(internalformat),
static_cast<GLsizei>(samples), InitState::MayNeedInit);
mDirtyChannel.signal(mState.mInitState);
onStateChange(context, angle::SubjectMessage::STATE_CHANGE);
return NoError();
}
......@@ -140,7 +140,7 @@ Error Renderbuffer::setStorageEGLImageTarget(const Context *context, egl::Image
mState.update(static_cast<GLsizei>(image->getWidth()), static_cast<GLsizei>(image->getHeight()),
Format(image->getFormat()), 0, image->sourceInitState());
mDirtyChannel.signal(mState.mInitState);
onStateChange(context, angle::SubjectMessage::STATE_CHANGE);
return NoError();
}
......
......@@ -247,7 +247,7 @@ Texture *TextureManager::getTexture(GLuint handle) const
return mObjectMap.query(handle);
}
void TextureManager::signalAllTexturesDirty() const
void TextureManager::signalAllTexturesDirty(const Context *context) const
{
for (const auto &texture : mObjectMap)
{
......@@ -255,7 +255,7 @@ void TextureManager::signalAllTexturesDirty() const
{
// We don't know if the Texture needs init, but that's ok, since it will only force
// a re-check, and will not initialize the pixels if it's not needed.
texture.second->signalDirty(InitState::MayNeedInit);
texture.second->signalDirty(context, InitState::MayNeedInit);
}
}
}
......
......@@ -157,7 +157,7 @@ class TextureManager : public TypedResourceManager<Texture, HandleAllocator, Tex
GLuint createTexture();
Texture *getTexture(GLuint handle) const;
void signalAllTexturesDirty() const;
void signalAllTexturesDirty(const Context *context) const;
Texture *checkTextureAllocation(rx::GLImplFactory *factory, GLuint handle, GLenum target)
{
......
......@@ -180,7 +180,7 @@ void State::initialize(const Context *context,
for (uint32_t textureIndex = 0; textureIndex < caps.maxCombinedTextureImageUnits;
++textureIndex)
{
mCompleteTextureBindings.emplace_back(OnAttachmentDirtyBinding(this, textureIndex));
mCompleteTextureBindings.emplace_back(this, textureIndex);
}
mSamplers.resize(caps.maxCombinedTextureImageUnits);
......@@ -2320,7 +2320,7 @@ void State::syncProgramTextures(const Context *context)
}
// Bind the texture unconditionally, to recieve completeness change notifications.
mCompleteTextureBindings[textureUnitIndex].bind(texture->getDirtyChannel());
mCompleteTextureBindings[textureUnitIndex].bind(texture);
mActiveTexturesMask.set(textureUnitIndex);
newActiveTextures.set(textureUnitIndex);
......@@ -2440,13 +2440,16 @@ const ImageUnit &State::getImageUnit(GLuint unit) const
}
// Handle a dirty texture event.
void State::signal(size_t textureIndex, InitState initState)
void State::onSubjectStateChange(const Context *context,
angle::SubjectIndex index,
angle::SubjectMessage message)
{
// Conservatively assume all textures are dirty.
// TODO(jmadill): More fine-grained update.
mDirtyObjects.set(DIRTY_OBJECT_PROGRAM_TEXTURES);
if (initState == InitState::MayNeedInit)
if (!mCompleteTextureCache[index] ||
mCompleteTextureCache[index]->initState() == InitState::MayNeedInit)
{
mCachedTexturesInitState = InitState::MayNeedInit;
}
......
......@@ -34,7 +34,7 @@ class VertexArray;
class Context;
struct Caps;
class State : public OnAttachmentDirtyReceiver, angle::NonCopyable
class State : public angle::ObserverInterface, angle::NonCopyable
{
public:
State();
......@@ -462,8 +462,10 @@ class State : public OnAttachmentDirtyReceiver, angle::NonCopyable
const std::vector<Texture *> &getCompleteTextureCache() const { return mCompleteTextureCache; }
ComponentTypeMask getCurrentValuesTypeMask() const { return mCurrentValuesTypeMask; }
// Handle a dirty texture event.
void signal(size_t textureIndex, InitState initState) override;
// Observer implementation.
void onSubjectStateChange(const Context *context,
angle::SubjectIndex index,
angle::SubjectMessage message) override;
Error clearUnclearedActiveTextures(const Context *context);
......@@ -527,11 +529,10 @@ class State : public OnAttachmentDirtyReceiver, angle::NonCopyable
// Texture Completeness Caching
// ----------------------------
// The texture completeness cache uses dirty bits to avoid having to scan the list
// of textures each draw call. This gl::State class implements OnAttachmentDirtyReceiver,
// and keeps an array of bindings to the Texture class. When the Textures are marked dirty,
// they send messages to the State class (and any Framebuffers they're attached to) via the
// State::signal method (see above). Internally this then invalidates the completeness cache.
// The texture completeness cache uses dirty bits to avoid having to scan the list of textures
// each draw call. This gl::State class implements angle::Observer interface. When subject
// Textures have state changes, messages reach 'State' (also any observing Framebuffers) via the
// onSubjectStateChange method (above). This then invalidates the completeness cache.
//
// Note this requires that we also invalidate the completeness cache manually on events like
// re-binding textures/samplers or a change in the program. For more information see the
......@@ -541,7 +542,7 @@ class State : public OnAttachmentDirtyReceiver, angle::NonCopyable
// Don't use BindingPointer because this cache is only valid within a draw call.
// Also stores a notification channel to the texture itself to handle texture change events.
std::vector<Texture *> mCompleteTextureCache;
std::vector<OnAttachmentDirtyBinding> mCompleteTextureBindings;
std::vector<angle::ObserverBinding> mCompleteTextureBindings;
InitState mCachedTexturesInitState;
using ActiveTextureMask = angle::BitSet<IMPLEMENTATION_MAX_ACTIVE_TEXTURES>;
ActiveTextureMask mActiveTexturesMask;
......
......@@ -151,12 +151,12 @@ Error Surface::destroyImpl(const Display *display)
return NoError();
}
void Surface::postSwap()
void Surface::postSwap(const gl::Context *context)
{
if (mRobustResourceInitialization && mSwapBehavior != EGL_BUFFER_PRESERVED)
{
mInitState = gl::InitState::MayNeedInit;
mDirtyChannel.signal(mInitState);
onStateChange(context, angle::SubjectMessage::STATE_CHANGE);
}
}
......@@ -231,14 +231,14 @@ EGLint Surface::getType() const
Error Surface::swap(const gl::Context *context)
{
ANGLE_TRY(mImplementation->swap(context));
postSwap();
postSwap(context);
return NoError();
}
Error Surface::swapWithDamage(const gl::Context *context, EGLint *rects, EGLint n_rects)
{
ANGLE_TRY(mImplementation->swapWithDamage(context, rects, n_rects));
postSwap();
postSwap(context);
return NoError();
}
......
......@@ -187,7 +187,7 @@ class Surface : public gl::FramebufferAttachmentObject
private:
Error destroyImpl(const Display *display);
void postSwap();
void postSwap(const gl::Context *context);
gl::InitState mInitState;
};
......
......@@ -913,9 +913,10 @@ egl::Stream *Texture::getBoundStream() const
return mBoundStream;
}
void Texture::signalDirty(InitState initState) const
void Texture::signalDirty(const Context *context, InitState initState)
{
mDirtyChannel.signal(initState);
mState.mInitState = initState;
onStateChange(context, angle::SubjectMessage::STATE_CHANGE);
invalidateCompletenessCache();
}
......@@ -941,7 +942,7 @@ Error Texture::setImage(const Context *context,
InitState initState = DetermineInitState(context, pixels);
mState.setImageDesc(target, level, ImageDesc(size, Format(internalFormat, type), initState));
signalDirty(initState);
signalDirty(context, initState);
return NoError();
}
......@@ -984,7 +985,7 @@ Error Texture::setCompressedImage(const Context *context,
InitState initState = DetermineInitState(context, pixels);
mState.setImageDesc(target, level, ImageDesc(size, Format(internalFormat), initState));
signalDirty(initState);
signalDirty(context, initState);
return NoError();
}
......@@ -1038,7 +1039,7 @@ Error Texture::copyImage(const Context *context,
Format(internalFormatInfo), InitState::Initialized));
// We need to initialize this texture only if the source attachment is not initialized.
signalDirty(InitState::Initialized);
signalDirty(context, InitState::Initialized);
return NoError();
}
......@@ -1094,7 +1095,7 @@ Error Texture::copyTexture(const Context *context,
target, level,
ImageDesc(sourceDesc.size, Format(internalFormatInfo), InitState::Initialized));
signalDirty(InitState::Initialized);
signalDirty(context, InitState::Initialized);
return NoError();
}
......@@ -1166,7 +1167,7 @@ Error Texture::setStorage(const Context *context,
mDirtyBits.set(DIRTY_BIT_BASE_LEVEL);
mDirtyBits.set(DIRTY_BIT_MAX_LEVEL);
signalDirty(InitState::MayNeedInit);
signalDirty(context, InitState::MayNeedInit);
return NoError();
}
......@@ -1193,7 +1194,7 @@ Error Texture::setStorageMultisample(const Context *context,
mState.setImageDescChainMultisample(size, Format(internalFormat), samples, fixedSampleLocations,
InitState::MayNeedInit);
signalDirty(InitState::MayNeedInit);
signalDirty(context, InitState::MayNeedInit);
return NoError();
}
......@@ -1233,7 +1234,7 @@ Error Texture::generateMipmap(const Context *context)
InitState::Initialized);
}
signalDirty(InitState::Initialized);
signalDirty(context, InitState::Initialized);
return NoError();
}
......@@ -1255,7 +1256,7 @@ Error Texture::bindTexImageFromSurface(const Context *context, egl::Surface *sur
Extents size(surface->getWidth(), surface->getHeight(), 1);
ImageDesc desc(size, surface->getBindTexImageFormat(), InitState::Initialized);
mState.setImageDesc(mState.mTarget, 0, desc);
signalDirty(InitState::Initialized);
signalDirty(context, InitState::Initialized);
return NoError();
}
......@@ -1268,7 +1269,7 @@ Error Texture::releaseTexImageFromSurface(const Context *context)
// Erase the image info for level 0
ASSERT(mState.mTarget == GL_TEXTURE_2D || mState.mTarget == GL_TEXTURE_RECTANGLE_ANGLE);
mState.clearImageDesc(mState.mTarget, 0);
signalDirty(InitState::Initialized);
signalDirty(context, InitState::Initialized);
return NoError();
}
......@@ -1299,7 +1300,7 @@ Error Texture::acquireImageFromStream(const Context *context,
Extents size(desc.width, desc.height, 1);
mState.setImageDesc(mState.mTarget, 0,
ImageDesc(size, Format(desc.internalFormat), InitState::Initialized));
signalDirty(InitState::Initialized);
signalDirty(context, InitState::Initialized);
return NoError();
}
......@@ -1311,7 +1312,7 @@ Error Texture::releaseImageFromStream(const Context *context)
// Set to incomplete
mState.clearImageDesc(mState.mTarget, 0);
signalDirty(InitState::Initialized);
signalDirty(context, InitState::Initialized);
return NoError();
}
......@@ -1348,7 +1349,7 @@ Error Texture::setEGLImageTarget(const Context *context, GLenum target, egl::Ima
mState.clearImageDescs();
mState.setImageDesc(target, 0, ImageDesc(size, imageTarget->getFormat(), initState));
signalDirty(initState);
signalDirty(context, initState);
return NoError();
}
......@@ -1445,7 +1446,7 @@ Error Texture::ensureInitialized(const Context *context)
}
if (anyDirty)
{
signalDirty(InitState::Initialized);
signalDirty(context, InitState::Initialized);
}
mState.mInitState = InitState::Initialized;
......
......@@ -349,7 +349,7 @@ class Texture final : public egl::ImageSibling,
egl::Surface *getBoundSurface() const;
egl::Stream *getBoundStream() const;
void signalDirty(InitState initState) const;
void signalDirty(const Context *context, InitState initState);
bool isSamplerComplete(const Context *context, const Sampler *optionalSampler);
......
......@@ -144,9 +144,7 @@ class Buffer11::BufferStorage : angle::NonCopyable
class Buffer11::NativeStorage : public Buffer11::BufferStorage
{
public:
NativeStorage(Renderer11 *renderer,
BufferUsage usage,
const OnBufferDataDirtyChannel *onStorageChanged);
NativeStorage(Renderer11 *renderer, BufferUsage usage, const angle::Subject *onStorageChanged);
~NativeStorage() override;
bool isCPUAccessible(GLbitfield access) const override;
......@@ -177,7 +175,7 @@ class Buffer11::NativeStorage : public Buffer11::BufferStorage
void clearSRVs();
d3d11::Buffer mBuffer;
const OnBufferDataDirtyChannel *mOnStorageChanged;
const angle::Subject *mOnStorageChanged;
std::map<DXGI_FORMAT, d3d11::ShaderResourceView> mBufferResourceViews;
};
......@@ -403,7 +401,7 @@ gl::Error Buffer11::setSubData(const gl::Context *context,
// Notify any vertex arrays that we have dirty data.
// TODO(jmadill): Use a more fine grained notification for data updates.
mDirectBroadcastChannel.signal(context);
mDirectSubject.onStateChange(context, angle::SubjectMessage::STATE_CHANGE);
}
mSize = std::max(mSize, requiredSize);
......@@ -474,7 +472,7 @@ gl::Error Buffer11::copySubData(const gl::Context *context,
invalidateStaticData(context);
// Also notify that direct buffers are dirty.
mDirectBroadcastChannel.signal(context);
mDirectSubject.onStateChange(context, angle::SubjectMessage::STATE_CHANGE);
return gl::NoError();
}
......@@ -760,7 +758,7 @@ Buffer11::BufferStorage *Buffer11::allocateStorage(BufferUsage usage)
return new EmulatedIndexedStorage(mRenderer);
case BUFFER_USAGE_INDEX:
case BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK:
return new NativeStorage(mRenderer, usage, &mDirectBroadcastChannel);
return new NativeStorage(mRenderer, usage, &mDirectSubject);
default:
return new NativeStorage(mRenderer, usage, nullptr);
}
......@@ -923,7 +921,7 @@ void Buffer11::initializeStaticData(const gl::Context *context)
BufferD3D::initializeStaticData(context);
// Notify when static data changes.
mStaticBroadcastChannel.signal(context);
mStaticSubject.onStateChange(context, angle::SubjectMessage::STATE_CHANGE);
}
void Buffer11::invalidateStaticData(const gl::Context *context)
......@@ -931,17 +929,17 @@ void Buffer11::invalidateStaticData(const gl::Context *context)
BufferD3D::invalidateStaticData(context);
// Notify when static data changes.
mStaticBroadcastChannel.signal(context);
mStaticSubject.onStateChange(context, angle::SubjectMessage::STATE_CHANGE);
}
OnBufferDataDirtyChannel *Buffer11::getStaticBroadcastChannel()
angle::Subject *Buffer11::getStaticSubject()
{
return &mStaticBroadcastChannel;
return &mStaticSubject;
}
OnBufferDataDirtyChannel *Buffer11::getDirectBroadcastChannel()
angle::Subject *Buffer11::getDirectSubject()
{
return &mDirectBroadcastChannel;
return &mDirectSubject;
}
void Buffer11::onCopyStorage(BufferStorage *dest, BufferStorage *source)
......@@ -991,7 +989,7 @@ gl::Error Buffer11::BufferStorage::setData(const uint8_t *data, size_t offset, s
Buffer11::NativeStorage::NativeStorage(Renderer11 *renderer,
BufferUsage usage,
const OnBufferDataDirtyChannel *onStorageChanged)
const angle::Subject *onStorageChanged)
: BufferStorage(renderer, usage), mBuffer(), mOnStorageChanged(onStorageChanged)
{
}
......@@ -1111,7 +1109,7 @@ gl::Error Buffer11::NativeStorage::resize(const gl::Context *context,
// Notify that the storage has changed.
if (mOnStorageChanged)
{
mOnStorageChanged->signal(context);
mOnStorageChanged->onStateChange(context, angle::SubjectMessage::STATE_CHANGE);
}
return gl::NoError();
......
......@@ -107,8 +107,8 @@ class Buffer11 : public BufferD3D
// We use two set of dirty events. Static buffers are marked dirty whenever
// data changes, because they must be re-translated. Direct buffers only need to be
// updated when the underlying ID3D11Buffer pointer changes - hopefully far less often.
OnBufferDataDirtyChannel *getStaticBroadcastChannel();
OnBufferDataDirtyChannel *getDirectBroadcastChannel();
angle::Subject *getStaticSubject();
angle::Subject *getDirectSubject();
private:
class BufferStorage;
......@@ -180,8 +180,8 @@ class Buffer11 : public BufferD3D
size_t mConstantBufferStorageAdditionalSize;
unsigned int mMaxConstantBufferLruCount;
OnBufferDataDirtyChannel mStaticBroadcastChannel;
OnBufferDataDirtyChannel mDirectBroadcastChannel;
angle::Subject mStaticSubject;
angle::Subject mDirectSubject;
};
} // namespace rx
......
......@@ -57,7 +57,7 @@ gl::Error MarkAttachmentsDirty(const gl::Context *context,
void UpdateCachedRenderTarget(const gl::Context *context,
const gl::FramebufferAttachment *attachment,
RenderTarget11 *&cachedRenderTarget,
OnRenderTargetDirtyBinding *channelBinding)
angle::ObserverBinding *channelBinding)
{
RenderTarget11 *newRenderTarget = nullptr;
if (attachment)
......@@ -71,9 +71,7 @@ void UpdateCachedRenderTarget(const gl::Context *context,
}
if (newRenderTarget != cachedRenderTarget)
{
OnRenderTargetDirtyChannel *channel =
(newRenderTarget ? newRenderTarget->getBroadcastChannel() : nullptr);
channelBinding->bind(channel);
channelBinding->bind(newRenderTarget);
cachedRenderTarget = newRenderTarget;
}
}
......@@ -451,9 +449,11 @@ void Framebuffer11::syncState(const gl::Context *context,
}
}
void Framebuffer11::signal(size_t channelID, const gl::Context *context)
void Framebuffer11::onSubjectStateChange(const gl::Context *context,
angle::SubjectIndex index,
angle::SubjectMessage message)
{
if (channelID == gl::IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS)
if (index == gl::IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS)
{
// Stencil is redundant in this case.
mInternalDirtyBits.set(gl::Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT);
......@@ -461,8 +461,8 @@ void Framebuffer11::signal(size_t channelID, const gl::Context *context)
}
else
{
mInternalDirtyBits.set(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 + channelID);
mCachedColorRenderTargets[channelID] = nullptr;
mInternalDirtyBits.set(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 + index);
mCachedColorRenderTargets[index] = nullptr;
}
// Notify the context we need to re-validate the RenderTarget.
......
......@@ -17,7 +17,7 @@ namespace rx
{
class Renderer11;
class Framebuffer11 : public FramebufferD3D, public OnRenderTargetDirtyReceiver
class Framebuffer11 : public FramebufferD3D, public angle::ObserverInterface
{
public:
Framebuffer11(const gl::FramebufferState &data, Renderer11 *renderer);
......@@ -52,7 +52,10 @@ class Framebuffer11 : public FramebufferD3D, public OnRenderTargetDirtyReceiver
bool hasAnyInternalDirtyBit() const;
void syncInternalState(const gl::Context *context);
void signal(size_t channelID, const gl::Context *context) override;
// Observer implementation.
void onSubjectStateChange(const gl::Context *context,
angle::SubjectIndex index,
angle::SubjectMessage message) override;
gl::Error getSamplePosition(size_t index, GLfloat *xy) const override;
......@@ -93,8 +96,8 @@ class Framebuffer11 : public FramebufferD3D, public OnRenderTargetDirtyReceiver
RenderTargetArray mCachedColorRenderTargets;
RenderTarget11 *mCachedDepthStencilRenderTarget;
std::vector<OnRenderTargetDirtyBinding> mColorRenderTargetsDirty;
OnRenderTargetDirtyBinding mDepthStencilRenderTargetDirty;
std::vector<angle::ObserverBinding> mColorRenderTargetsDirty;
angle::ObserverBinding mDepthStencilRenderTargetDirty;
gl::Framebuffer::DirtyBits mInternalDirtyBits;
};
......
......@@ -200,15 +200,15 @@ RenderTarget11::RenderTarget11(const d3d11::Format &formatSet) : mFormatSet(form
RenderTarget11::~RenderTarget11()
{
ASSERT(mBroadcastChannel.empty());
ASSERT(!hasObservers());
}
void RenderTarget11::signalDirty(const gl::Context *context)
{
mBroadcastChannel.signal(context);
onStateChange(context, angle::SubjectMessage::STATE_CHANGE);
// Clear the list. We can't do this in the receiver because it would mutate during iteration.
mBroadcastChannel.reset();
resetObservers();
}
TextureRenderTarget11::TextureRenderTarget11(d3d11::RenderTargetView &&rtv,
......
......@@ -20,7 +20,7 @@ namespace rx
class SwapChain11;
class Renderer11;
class RenderTarget11 : public RenderTargetD3D
class RenderTarget11 : public RenderTargetD3D, public angle::Subject
{
public:
RenderTarget11(const d3d11::Format &formatSet);
......@@ -35,12 +35,10 @@ class RenderTarget11 : public RenderTargetD3D
virtual unsigned int getSubresourceIndex() const = 0;
void signalDirty(const gl::Context *context) override;
OnRenderTargetDirtyChannel *getBroadcastChannel() { return &mBroadcastChannel; }
const d3d11::Format &getFormatSet() const { return mFormatSet; }
protected:
OnRenderTargetDirtyChannel mBroadcastChannel;
const d3d11::Format &mFormatSet;
};
......
......@@ -3263,8 +3263,10 @@ StateManager11::OnConstantBufferDirtyReceiver::~OnConstantBufferDirtyReceiver()
{
}
void StateManager11::OnConstantBufferDirtyReceiver::signal(size_t messageID,
const gl::Context *context)
void StateManager11::OnConstantBufferDirtyReceiver::onSubjectStateChange(
const gl::Context *context,
angle::SubjectIndex index,
angle::SubjectMessage message)
{
StateManager11 *stateManager = GetImplAs<Context11>(context)->getRenderer()->getStateManager();
stateManager->invalidateProgramUniformBuffers();
......@@ -3274,24 +3276,24 @@ void StateManager11::OnConstantBufferDirtyReceiver::bindVS(size_t index, Buffer1
{
ASSERT(buffer);
ASSERT(index < mBindingsVS.size());
mBindingsVS[index].bind(buffer->getDirectBroadcastChannel());
mBindingsVS[index].bind(buffer->getDirectSubject());
}
void StateManager11::OnConstantBufferDirtyReceiver::bindPS(size_t index, Buffer11 *buffer)
{
ASSERT(buffer);
ASSERT(index < mBindingsPS.size());
mBindingsPS[index].bind(buffer->getDirectBroadcastChannel());
mBindingsPS[index].bind(buffer->getDirectSubject());
}
void StateManager11::OnConstantBufferDirtyReceiver::reset()
{
for (OnBufferDataDirtyBinding &vsBinding : mBindingsVS)
for (angle::ObserverBinding &vsBinding : mBindingsVS)
{
vsBinding.bind(nullptr);
}
for (OnBufferDataDirtyBinding &psBinding : mBindingsPS)
for (angle::ObserverBinding &psBinding : mBindingsPS)
{
psBinding.bind(nullptr);
}
......
......@@ -566,21 +566,23 @@ class StateManager11 final : angle::NonCopyable
FragmentConstantBufferArray<GLintptr> mCurrentConstantBufferPSOffset;
FragmentConstantBufferArray<GLsizeiptr> mCurrentConstantBufferPSSize;
class OnConstantBufferDirtyReceiver : public OnBufferDataDirtyReceiver
class OnConstantBufferDirtyReceiver : public angle::ObserverInterface
{
public:
OnConstantBufferDirtyReceiver();
~OnConstantBufferDirtyReceiver() override;
void signal(size_t messageID, const gl::Context *context) override;
void onSubjectStateChange(const gl::Context *context,
angle::SubjectIndex index,
angle::SubjectMessage message) override;
void reset();
void bindVS(size_t index, Buffer11 *buffer);
void bindPS(size_t index, Buffer11 *buffer);
private:
std::vector<OnBufferDataDirtyBinding> mBindingsVS;
std::vector<OnBufferDataDirtyBinding> mBindingsPS;
std::vector<angle::ObserverBinding> mBindingsVS;
std::vector<angle::ObserverBinding> mBindingsPS;
};
OnConstantBufferDirtyReceiver mOnConstantBufferDirtyReceiver;
......
......@@ -21,17 +21,16 @@ namespace rx
namespace
{
OnBufferDataDirtyChannel *GetBufferBroadcastChannel(Buffer11 *buffer11,
IndexStorageType storageType)
angle::Subject *GetBufferSubject(Buffer11 *buffer11, IndexStorageType storageType)
{
switch (storageType)
{
case IndexStorageType::Direct:
return buffer11->getDirectBroadcastChannel();
return buffer11->getDirectSubject();
case IndexStorageType::Static:
return buffer11->getStaticBroadcastChannel();
return buffer11->getStaticSubject();
case IndexStorageType::Dynamic:
return buffer11 ? buffer11->getStaticBroadcastChannel() : nullptr;
return buffer11 ? buffer11->getStaticSubject() : nullptr;
default:
UNREACHABLE();
return nullptr;
......@@ -157,10 +156,10 @@ bool VertexArray11::updateElementArrayStorage(const gl::Context *context,
{
Buffer11 *newBuffer11 = SafeGetImplAs<Buffer11>(newBuffer);
auto *newChannel = GetBufferBroadcastChannel(newBuffer11, newStorageType);
angle::Subject *subject = GetBufferSubject(newBuffer11, newStorageType);
mCurrentElementArrayStorage = newStorageType;
mOnElementArrayBufferDataDirty.bind(newChannel);
mOnElementArrayBufferDataDirty.bind(subject);
needsTranslation = true;
}
......@@ -222,7 +221,7 @@ void VertexArray11::updateVertexAttribStorage(const gl::Context *context, size_t
if (oldBuffer11 != newBuffer11 || oldStorageType != newStorageType)
{
OnBufferDataDirtyChannel *newChannel = nullptr;
angle::Subject *subject = nullptr;
if (newStorageType == VertexStorageType::CURRENT_VALUE)
{
......@@ -235,11 +234,11 @@ void VertexArray11::updateVertexAttribStorage(const gl::Context *context, size_t
switch (newStorageType)
{
case VertexStorageType::DIRECT:
newChannel = newBuffer11->getDirectBroadcastChannel();
subject = newBuffer11->getDirectSubject();
break;
case VertexStorageType::STATIC:
case VertexStorageType::DYNAMIC:
newChannel = newBuffer11->getStaticBroadcastChannel();
subject = newBuffer11->getStaticSubject();
break;
default:
UNREACHABLE();
......@@ -247,7 +246,7 @@ void VertexArray11::updateVertexAttribStorage(const gl::Context *context, size_t
}
}
mOnArrayBufferDataDirty[attribIndex].bind(newChannel);
mOnArrayBufferDataDirty[attribIndex].bind(subject);
mCurrentArrayBuffers[attribIndex].set(context, binding.getBuffer().get());
}
}
......@@ -348,9 +347,11 @@ const std::vector<TranslatedAttribute> &VertexArray11::getTranslatedAttribs() co
return mTranslatedAttribs;
}
void VertexArray11::signal(size_t channelID, const gl::Context *context)
void VertexArray11::onSubjectStateChange(const gl::Context *context,
angle::SubjectIndex index,
angle::SubjectMessage message)
{
if (channelID == mAttributeStorageTypes.size())
if (index == mAttributeStorageTypes.size())
{
mCachedIndexInfoValid = false;
mLastElementType = GL_NONE;
......@@ -358,10 +359,10 @@ void VertexArray11::signal(size_t channelID, const gl::Context *context)
}
else
{
ASSERT(mAttributeStorageTypes[channelID] != VertexStorageType::CURRENT_VALUE);
ASSERT(mAttributeStorageTypes[index] != VertexStorageType::CURRENT_VALUE);
// This can change a buffer's storage, we'll need to re-check.
mAttribsToUpdate.set(channelID);
mAttribsToUpdate.set(index);
// Changing the vertex attribute state can affect the vertex shader.
Renderer11 *renderer = GetImplAs<Context11>(context)->getRenderer();
......
......@@ -19,7 +19,7 @@ namespace rx
{
class Renderer11;
class VertexArray11 : public VertexArrayImpl, public OnBufferDataDirtyReceiver
class VertexArray11 : public VertexArrayImpl, public angle::ObserverInterface
{
public:
VertexArray11(const gl::VertexArrayState &data);
......@@ -38,8 +38,10 @@ class VertexArray11 : public VertexArrayImpl, public OnBufferDataDirtyReceiver
const std::vector<TranslatedAttribute> &getTranslatedAttribs() const;
// SignalReceiver implementation
void signal(size_t channelID, const gl::Context *context) override;
// Observer implementation
void onSubjectStateChange(const gl::Context *context,
angle::SubjectIndex index,
angle::SubjectMessage message) override;
Serial getCurrentStateSerial() const { return mCurrentStateSerial; }
......@@ -78,8 +80,8 @@ class VertexArray11 : public VertexArrayImpl, public OnBufferDataDirtyReceiver
std::vector<gl::BindingPointer<gl::Buffer>> mCurrentArrayBuffers;
gl::BindingPointer<gl::Buffer> mCurrentElementArrayBuffer;
std::vector<OnBufferDataDirtyBinding> mOnArrayBufferDataDirty;
OnBufferDataDirtyBinding mOnElementArrayBufferDataDirty;
std::vector<angle::ObserverBinding> mOnArrayBufferDataDirty;
angle::ObserverBinding mOnElementArrayBufferDataDirty;
Serial mCurrentStateSerial;
......
......@@ -424,16 +424,6 @@ IndexStorageType ClassifyIndexStorage(const gl::State &glState,
unsigned int offset,
bool *needsTranslation);
// Used for state change notifications between buffers and vertex arrays.
using OnBufferDataDirtyBinding = angle::ChannelBinding<size_t, const gl::Context *>;
using OnBufferDataDirtyChannel = angle::BroadcastChannel<size_t, const gl::Context *>;
using OnBufferDataDirtyReceiver = angle::SignalReceiver<size_t, const gl::Context *>;
// Used for state change notifications between RenderTarget11 and Framebuffer11.
using OnRenderTargetDirtyBinding = angle::ChannelBinding<size_t, const gl::Context *>;
using OnRenderTargetDirtyChannel = angle::BroadcastChannel<size_t, const gl::Context *>;
using OnRenderTargetDirtyReceiver = angle::SignalReceiver<size_t, const gl::Context *>;
} // namespace rx
#endif // LIBANGLE_RENDERER_D3D_D3D11_RENDERER11_UTILS_H_
//
// Copyright 2018 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// signal_utils:
// Implements the Observer pattern for sending state change notifications
// from Subject objects to dependent Observer objects.
//
// See design document:
// https://docs.google.com/document/d/15Edfotqg6_l1skTEL8ADQudF_oIdNa7i8Po43k6jMd4/
#include "libANGLE/signal_utils.h"
#include "common/debug.h"
namespace angle
{
// Observer implementation.
ObserverInterface::~ObserverInterface() = default;
// Subject implementation.
Subject::Subject()
{
}
Subject::~Subject()
{
resetObservers();
}
bool Subject::hasObservers() const
{
return !mObservers.empty();
}
void Subject::addObserver(ObserverBinding *observer)
{
ASSERT(std::find(mObservers.begin(), mObservers.end(), observer) == mObservers.end());
mObservers.push_back(observer);
}
void Subject::removeObserver(ObserverBinding *observer)
{
auto iter = std::find(mObservers.begin(), mObservers.end(), observer);
ASSERT(iter != mObservers.end());
mObservers.erase(iter);
}
void Subject::onStateChange(const gl::Context *context, SubjectMessage message) const
{
if (mObservers.empty())
return;
for (const angle::ObserverBinding *receiver : mObservers)
{
receiver->onStateChange(context, message);
}
}
void Subject::resetObservers()
{
for (angle::ObserverBinding *observer : mObservers)
{
observer->onSubjectReset();
}
mObservers.clear();
}
// ObserverBinding implementation.
ObserverBinding::ObserverBinding(ObserverInterface *observer, SubjectIndex index)
: mSubject(nullptr), mObserver(observer), mIndex(index)
{
ASSERT(observer);
}
ObserverBinding::~ObserverBinding()
{
reset();
}
ObserverBinding::ObserverBinding(const ObserverBinding &other) = default;
ObserverBinding &ObserverBinding::operator=(const ObserverBinding &other) = default;
void ObserverBinding::bind(Subject *subject)
{
ASSERT(mObserver);
if (mSubject)
{
mSubject->removeObserver(this);
}
mSubject = subject;
if (mSubject)
{
mSubject->addObserver(this);
}
}
void ObserverBinding::reset()
{
bind(nullptr);
}
void ObserverBinding::onStateChange(const gl::Context *context, SubjectMessage message) const
{
mObserver->onSubjectStateChange(context, mIndex, message);
}
void ObserverBinding::onSubjectReset()
{
mSubject = nullptr;
}
} // namespace angle
......@@ -4,184 +4,84 @@
// found in the LICENSE file.
//
// signal_utils:
// Helper classes for tracking dependent state changes between objects.
// These changes are signaled to the dependent class via channels.
// Implements the Observer pattern for sending state change notifications
// from Subject objects to dependent Observer objects.
//
// See design document:
// https://docs.google.com/document/d/15Edfotqg6_l1skTEL8ADQudF_oIdNa7i8Po43k6jMd4/
#ifndef LIBANGLE_SIGNAL_UTILS_H_
#define LIBANGLE_SIGNAL_UTILS_H_
#include <set>
#include "common/angleutils.h"
#include "common/debug.h"
namespace angle
namespace gl
{
class Context;
} // namespace gl
// Interface that the depending class inherits from.
template <typename ChannelID = uint32_t, typename... MessageT>
class SignalReceiver
namespace angle
{
public:
virtual ~SignalReceiver() = default;
virtual void signal(ChannelID channelID, MessageT... message) = 0;
};
template <typename ChannelID, typename... MessageT>
class ChannelBinding;
using SubjectIndex = size_t;
// The host class owns the channel. It uses the channel to fire signals to the receiver.
template <typename ChannelID = uint32_t, typename... MessageT>
class BroadcastChannel final : NonCopyable
enum class SubjectMessage
{
public:
BroadcastChannel();
~BroadcastChannel();
void signal(MessageT... message) const;
void reset();
bool empty() const;
private:
// Only the ChannelBinding class should add or remove receivers.
friend class ChannelBinding<ChannelID, MessageT...>;
void addReceiver(ChannelBinding<ChannelID, MessageT...> *receiver);
void removeReceiver(ChannelBinding<ChannelID, MessageT...> *receiver);
std::vector<ChannelBinding<ChannelID, MessageT...> *> mReceivers;
STATE_CHANGE,
};
template <typename ChannelID, typename... MessageT>
BroadcastChannel<ChannelID, MessageT...>::BroadcastChannel()
// The observing class inherits from this interface class.
class ObserverInterface
{
}
template <typename ChannelID, typename... MessageT>
BroadcastChannel<ChannelID, MessageT...>::~BroadcastChannel()
{
reset();
}
public:
virtual ~ObserverInterface();
virtual void onSubjectStateChange(const gl::Context *context,
SubjectIndex index,
SubjectMessage message) = 0;
};
template <typename ChannelID, typename... MessageT>
void BroadcastChannel<ChannelID, MessageT...>::addReceiver(
ChannelBinding<ChannelID, MessageT...> *receiver)
{
ASSERT(std::find(mReceivers.begin(), mReceivers.end(), receiver) == mReceivers.end());
mReceivers.push_back(receiver);
}
class ObserverBinding;
template <typename ChannelID, typename... MessageT>
void BroadcastChannel<ChannelID, MessageT...>::removeReceiver(
ChannelBinding<ChannelID, MessageT...> *receiver)
// Maintains a list of observer bindings. Sends update messages to the observer.
class Subject : NonCopyable
{
auto iter = std::find(mReceivers.begin(), mReceivers.end(), receiver);
ASSERT(iter != mReceivers.end());
mReceivers.erase(iter);
}
public:
Subject();
virtual ~Subject();
template <typename ChannelID, typename... MessageT>
void BroadcastChannel<ChannelID, MessageT...>::signal(MessageT... message) const
{
if (mReceivers.empty())
return;
void onStateChange(const gl::Context *context, SubjectMessage message) const;
bool hasObservers() const;
void resetObservers();
for (const auto *receiver : mReceivers)
{
receiver->signal(message...);
}
}
private:
// Only the ObserverBinding class should add or remove observers.
friend class ObserverBinding;
void addObserver(ObserverBinding *observer);
void removeObserver(ObserverBinding *observer);
template <typename ChannelID, typename... MessageT>
void BroadcastChannel<ChannelID, MessageT...>::reset()
{
for (auto receiver : mReceivers)
{
receiver->onChannelClosed();
}
mReceivers.clear();
}
template <typename ChannelID, typename... MessageT>
bool BroadcastChannel<ChannelID, MessageT...>::empty() const
{
return mReceivers.empty();
}
std::vector<ObserverBinding *> mObservers;
};
// The dependent class keeps bindings to the host's BroadcastChannel.
template <typename ChannelID = uint32_t, typename... MessageT>
class ChannelBinding final
// Keeps a binding between a Subject and Observer, with a specific subject index.
class ObserverBinding final
{
public:
ChannelBinding(SignalReceiver<ChannelID, MessageT...> *receiver, ChannelID channelID);
~ChannelBinding();
ChannelBinding(const ChannelBinding &other) = default;
ChannelBinding &operator=(const ChannelBinding &other) = default;
ObserverBinding(ObserverInterface *observer, SubjectIndex index);
~ObserverBinding();
ObserverBinding(const ObserverBinding &other);
ObserverBinding &operator=(const ObserverBinding &other);
void bind(BroadcastChannel<ChannelID, MessageT...> *channel);
void bind(Subject *subject);
void reset();
void signal(MessageT... message) const;
void onChannelClosed();
void onStateChange(const gl::Context *context, SubjectMessage message) const;
void onSubjectReset();
private:
BroadcastChannel<ChannelID, MessageT...> *mChannel;
SignalReceiver<ChannelID, MessageT...> *mReceiver;
ChannelID mChannelID;
Subject *mSubject;
ObserverInterface *mObserver;
SubjectIndex mIndex;
};
template <typename ChannelID, typename... MessageT>
ChannelBinding<ChannelID, MessageT...>::ChannelBinding(
SignalReceiver<ChannelID, MessageT...> *receiver,
ChannelID channelID)
: mChannel(nullptr), mReceiver(receiver), mChannelID(channelID)
{
ASSERT(receiver);
}
template <typename ChannelID, typename... MessageT>
ChannelBinding<ChannelID, MessageT...>::~ChannelBinding()
{
reset();
}
template <typename ChannelID, typename... MessageT>
void ChannelBinding<ChannelID, MessageT...>::bind(BroadcastChannel<ChannelID, MessageT...> *channel)
{
ASSERT(mReceiver);
if (mChannel)
{
mChannel->removeReceiver(this);
}
mChannel = channel;
if (mChannel)
{
mChannel->addReceiver(this);
}
}
template <typename ChannelID, typename... MessageT>
void ChannelBinding<ChannelID, MessageT...>::reset()
{
bind(nullptr);
}
template <typename ChannelID, typename... MessageT>
void ChannelBinding<ChannelID, MessageT...>::signal(MessageT... message) const
{
mReceiver->signal(mChannelID, message...);
}
template <typename ChannelID, typename... MessageT>
void ChannelBinding<ChannelID, MessageT...>::onChannelClosed()
{
mChannel = nullptr;
}
} // namespace angle
#endif // LIBANGLE_SIGNAL_UTILS_H_
......@@ -4,7 +4,7 @@
// found in the LICENSE file.
//
// signal_utils_unittest:
// Unit tests for signals and related utils.
// Unit tests for Observers and related classes.
#include <gtest/gtest.h>
......@@ -16,23 +16,28 @@ using namespace testing;
namespace
{
struct SignalThing : public SignalReceiver<>
struct ObserverClass : public ObserverInterface
{
void signal(uint32_t channelID) override { wasSignaled = true; }
bool wasSignaled = false;
void onSubjectStateChange(const gl::Context *context,
SubjectIndex index,
SubjectMessage message) override
{
wasNotified = true;
}
bool wasNotified = false;
};
// Test that broadcast signals work.
TEST(SignalTest, BroadcastSignals)
// Test that Observer/Subject state change notifications work.
TEST(ObserverTest, BasicUsage)
{
BroadcastChannel<> channel;
SignalThing thing;
ChannelBinding<> binding(&thing, 0u);
binding.bind(&channel);
ASSERT_FALSE(thing.wasSignaled);
channel.signal();
ASSERT_TRUE(thing.wasSignaled);
Subject subject;
ObserverClass observer;
ObserverBinding binding(&observer, 0u);
binding.bind(&subject);
ASSERT_FALSE(observer.wasNotified);
subject.onStateChange(nullptr, SubjectMessage::STATE_CHANGE);
ASSERT_TRUE(observer.wasNotified);
}
} // anonymous namespace
......@@ -284,6 +284,7 @@
'libANGLE/renderer/load_functions_table_autogen.cpp',
'libANGLE/renderer/renderer_utils.cpp',
'libANGLE/renderer/renderer_utils.h',
'libANGLE/signal_utils.cpp',
'libANGLE/signal_utils.h',
'libANGLE/validationEGL.cpp',
'libANGLE/validationEGL.h',
......
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