Hash :
ecc378cc
Author :
Date :
2025-03-03T16:43:33
Reland "Dont use Subject/Observer for SwapchainImageChanged" This is reland of the following CL https://chromium-review.googlesource.com/c/angle/angle/+/6319893 Because we do deferred ANI (VkAcquireNextImage) call until image is needed, we need a way to force Context to go through FramebufferVk::syncState call (FramebufferVk::syncState calls WindowSurfaceVk::getAttachmentRenderTarget, which end up calling ANI. Right now we uses subject/observer mechanism, by sending angle::SubjectMessage::SwapchainImageChanged to all observers of WindowSurfaceVk. In this case it is egl::Surface. Then eglSurface redirects this message to its observers, which are all gl::Framebuffer's attachments: color, depth, stencil. Even though only color attachment needs to be notified, but because we don't have a separate list of observers, depth/stencil attachment also receive the notification and they early out. Then gl::Framebuffer sets DIRTY_BIT_COLOR_BUFFER_CONTENTS_0 dirty bit and send the angle::SubjectMessage::DirtyBitsFlagged to Context, which dirty DrawFBO and ReadFBO and dirty cached state. Note that this is specific for swap image changed case, there is no surface property change (surface property change will still trigger the subject/observer message with SubjectMessage::SubjectChanged message, but this occurs rarely). This gets worse for apps that uses multiple contexts, for the example pokemon_masters_ex has three contexts, each context has its own default frame buffer that attach to the same surface, and we never remove non-current context from the observer list. This end up with egl::Surface has 12 observers and for every frame, it loop over the list of 12 observers and send message (virtual function call) to each of them. Color attachment also ends up sending two messages to Context, one for Read FBO and another for Draw FBO. There are total 21 virtual function calls. Even for single context usage, you have 6 virtual function calls, for every frame. EGL spec says "an EGLSurface must be current on only one thread at a time", any other context must call EGLMakeCurrent in order to use this surface, which will add all necessary dirty bits at that time. So we really only need to notify current context. In this CL, SwapchainImageChanged no longer uses subject/observer mechanism, so this message is removed. This CL still uses subject/observer mechanism to send DirtyBitsFlagged from Framebuffer back to context. We could call setDrawFramebufferDirty and setReadFramebufferDirty directly, but that will require to remove the "const" decoration out of gl::Context which generates too much code diff, so onStateChange(angle::SubjectMessage::DirtyBitsFlagged) is still used. Bug: angleproject:400711938 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/6319893 Commit-Queue: Charlie Lao <cclao@google.com> Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Yuxin Hu <yuxinhu@google.com> Change-Id: I017b0e8934b5194a520828fa5c4af1d6e3ef9aac Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/6404621
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
//
// Copyright 2016 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.
//
// Observer:
// 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_OBSERVER_H_
#define LIBANGLE_OBSERVER_H_
#include "common/FastVector.h"
#include "common/angleutils.h"
#include "libANGLE/Constants.h"
namespace angle
{
template <typename HaystackT, typename NeedleT>
bool IsInContainer(const HaystackT &haystack, const NeedleT &needle)
{
return std::find(haystack.begin(), haystack.end(), needle) != haystack.end();
}
using SubjectIndex = size_t;
// Messages are used to distinguish different Subject events that get sent to a single Observer.
// It could be possible to improve the handling by using different callback functions instead
// of a single handler function. But in some cases we want to share a single binding between
// Observer and Subject and handle different types of events.
enum class SubjectMessage
{
// Used by gl::VertexArray to notify gl::Context of a gl::Buffer binding count change. Triggers
// a validation cache update. Also used by gl::Texture to notify gl::Framebuffer of loops.
BindingChanged,
// Only the contents (pixels, bytes, etc) changed in this Subject. Distinct from the object
// storage.
ContentsChanged,
// Sent by gl::Sampler, gl::Texture, gl::Framebuffer and others to notifiy gl::Context. This
// flag indicates to call syncState before next use.
DirtyBitsFlagged,
// Generic state change message. Used in multiple places for different purposes.
SubjectChanged,
// Indicates a bound gl::Buffer is now mapped or unmapped. Passed from gl::Buffer, through
// gl::VertexArray, into gl::Context. Used to track validation.
SubjectMapped,
SubjectUnmapped,
// Indicates a bound buffer's storage was reallocated due to glBufferData call or optimizations
// to prevent having to flush pending commands and waiting for the GPU to become idle.
InternalMemoryAllocationChanged,
// Indicates an external change to the default framebuffer.
SurfaceChanged,
// Indicates a separable program's textures or images changed in the ProgramExecutable.
ProgramTextureOrImageBindingChanged,
// Indicates a program or pipeline is being re-linked. This is used to make sure the Context or
// ProgramPipeline that reference the program/pipeline wait for it to finish linking.
ProgramUnlinked,
// Indicates a program or pipeline was successfully re-linked.
ProgramRelinked,
// Indicates a separable program's sampler uniforms were updated.
SamplerUniformsUpdated,
// Indicates a program's uniform block binding has changed (one message per binding)
ProgramUniformBlockBindingZeroUpdated,
ProgramUniformBlockBindingLastUpdated = ProgramUniformBlockBindingZeroUpdated +
gl::IMPLEMENTATION_MAX_COMBINED_SHADER_UNIFORM_BUFFERS -
1,
// Indicates a Storage of back-end in gl::Texture has been released.
StorageReleased,
// Sent when the GLuint ID for a gl::Texture is being deleted via glDeleteTextures. The
// texture may stay alive due to orphaning, but will no longer be directly accessible by the GL
// API.
TextureIDDeleted,
// Indicates that all pending updates are complete in the subject.
InitializationComplete,
// Indicates a change in foveated rendering state in the subject.
FoveatedRenderingStateChanged,
};
inline bool IsProgramUniformBlockBindingUpdatedMessage(SubjectMessage message)
{
return message >= SubjectMessage::ProgramUniformBlockBindingZeroUpdated &&
message <= SubjectMessage::ProgramUniformBlockBindingLastUpdated;
}
inline SubjectMessage ProgramUniformBlockBindingUpdatedMessageFromIndex(uint32_t blockIndex)
{
return static_cast<SubjectMessage>(
static_cast<uint32_t>(SubjectMessage::ProgramUniformBlockBindingZeroUpdated) + blockIndex);
}
inline uint32_t ProgramUniformBlockBindingUpdatedMessageToIndex(SubjectMessage message)
{
return static_cast<uint32_t>(message) -
static_cast<uint32_t>(SubjectMessage::ProgramUniformBlockBindingZeroUpdated);
}
// The observing class inherits from this interface class.
class ObserverInterface
{
public:
virtual ~ObserverInterface();
virtual void onSubjectStateChange(SubjectIndex index, SubjectMessage message) = 0;
};
class ObserverBindingBase
{
public:
ObserverBindingBase(ObserverInterface *observer, SubjectIndex subjectIndex)
: mObserver(observer), mIndex(subjectIndex)
{}
virtual ~ObserverBindingBase() {}
ObserverBindingBase(const ObserverBindingBase &other) = default;
ObserverBindingBase &operator=(const ObserverBindingBase &other) = default;
ObserverInterface *getObserver() const { return mObserver; }
SubjectIndex getSubjectIndex() const { return mIndex; }
virtual void onSubjectReset() {}
private:
ObserverInterface *mObserver;
SubjectIndex mIndex;
};
constexpr size_t kMaxFixedObservers = 8;
// Maintains a list of observer bindings. Sends update messages to the observer.
class Subject : NonCopyable
{
public:
Subject();
virtual ~Subject();
void onStateChange(SubjectMessage message) const;
bool hasObservers() const;
void resetObservers();
ANGLE_INLINE size_t getObserversCount() const { return mObservers.size(); }
ANGLE_INLINE void addObserver(ObserverBindingBase *observer)
{
ASSERT(!IsInContainer(mObservers, observer));
mObservers.push_back(observer);
}
ANGLE_INLINE void removeObserver(ObserverBindingBase *observer)
{
ASSERT(IsInContainer(mObservers, observer));
mObservers.remove_and_permute(observer);
}
private:
// Keep a short list of observers so we can allocate/free them quickly. But since we support
// unlimited bindings, have a spill-over list of that uses dynamic allocation.
angle::FastVector<ObserverBindingBase *, kMaxFixedObservers> mObservers;
};
// Keeps a binding between a Subject and Observer, with a specific subject index.
class ObserverBinding final : public ObserverBindingBase
{
public:
ObserverBinding();
ObserverBinding(ObserverInterface *observer, SubjectIndex index);
~ObserverBinding() override;
ObserverBinding(const ObserverBinding &other);
ObserverBinding &operator=(const ObserverBinding &other);
void bind(Subject *subject);
ANGLE_INLINE void reset() { bind(nullptr); }
void onStateChange(SubjectMessage message) const;
void onSubjectReset() override;
ANGLE_INLINE const Subject *getSubject() const { return mSubject; }
ANGLE_INLINE void assignSubject(Subject *subject) { mSubject = subject; }
private:
Subject *mSubject;
};
} // namespace angle
#endif // LIBANGLE_OBSERVER_H_