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 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
//
// 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.
//
// SurfaceEGL.cpp: EGL implementation of egl::Surface
#include "libANGLE/renderer/gl/egl/SurfaceEGL.h"
#include "common/debug.h"
#include "libANGLE/Display.h"
namespace rx
{
SurfaceEGL::SurfaceEGL(const egl::SurfaceState &state, const FunctionsEGL *egl, EGLConfig config)
: SurfaceGL(state),
mEGL(egl),
mConfig(config),
mSurface(EGL_NO_SURFACE),
mHasSwapBuffersWithDamage(mEGL->hasExtension("EGL_KHR_swap_buffers_with_damage"))
{}
SurfaceEGL::~SurfaceEGL()
{
if (mSurface != EGL_NO_SURFACE)
{
EGLBoolean success = mEGL->destroySurface(mSurface);
ASSERT(success == EGL_TRUE);
}
}
egl::Error SurfaceEGL::makeCurrent(const gl::Context *context)
{
// Handling of makeCurrent is done in DisplayEGL
return egl::NoError();
}
egl::Error SurfaceEGL::swap(const gl::Context *context, SurfaceSwapFeedback *feedback)
{
egl::Display::GetCurrentThreadUnlockedTailCall()->add(
[egl = mEGL, surface = mSurface](void *resultOut) {
ANGLE_UNUSED_VARIABLE(resultOut);
*static_cast<EGLBoolean *>(resultOut) = egl->swapBuffers(surface);
});
return egl::NoError();
}
egl::Error SurfaceEGL::swapWithDamage(const gl::Context *context,
const EGLint *rects,
EGLint n_rects,
SurfaceSwapFeedback *feedback)
{
if (mHasSwapBuffersWithDamage)
{
egl::Display::GetCurrentThreadUnlockedTailCall()->add(
[egl = mEGL, surface = mSurface, rects, n_rects](void *resultOut) {
ANGLE_UNUSED_VARIABLE(resultOut);
*static_cast<EGLBoolean *>(resultOut) =
egl->swapBuffersWithDamageKHR(surface, rects, n_rects);
});
}
else
{
egl::Display::GetCurrentThreadUnlockedTailCall()->add(
[egl = mEGL, surface = mSurface](void *resultOut) {
ANGLE_UNUSED_VARIABLE(resultOut);
*static_cast<EGLBoolean *>(resultOut) = egl->swapBuffers(surface);
});
}
return egl::NoError();
}
egl::Error SurfaceEGL::postSubBuffer(const gl::Context *context,
EGLint x,
EGLint y,
EGLint width,
EGLint height)
{
UNIMPLEMENTED();
return egl::Error(EGL_BAD_SURFACE);
}
egl::Error SurfaceEGL::setPresentationTime(EGLnsecsANDROID time)
{
EGLBoolean success = mEGL->presentationTimeANDROID(mSurface, time);
if (success == EGL_FALSE)
{
return egl::Error(mEGL->getError(), "eglPresentationTimeANDROID failed");
}
return egl::NoError();
}
egl::Error SurfaceEGL::querySurfacePointerANGLE(EGLint attribute, void **value)
{
UNIMPLEMENTED();
return egl::Error(EGL_BAD_SURFACE);
}
egl::Error SurfaceEGL::bindTexImage(const gl::Context *context, gl::Texture *texture, EGLint buffer)
{
EGLBoolean success = mEGL->bindTexImage(mSurface, buffer);
if (success == EGL_FALSE)
{
return egl::Error(mEGL->getError(), "eglBindTexImage failed");
}
return egl::NoError();
}
egl::Error SurfaceEGL::releaseTexImage(const gl::Context *context, EGLint buffer)
{
EGLBoolean success = mEGL->releaseTexImage(mSurface, buffer);
if (success == EGL_FALSE)
{
return egl::Error(mEGL->getError(), "eglReleaseTexImage failed");
}
return egl::NoError();
}
void SurfaceEGL::setSwapInterval(const egl::Display *display, EGLint interval)
{
EGLBoolean success = mEGL->swapInterval(interval);
if (success == EGL_FALSE)
{
ERR() << "eglSwapInterval error " << egl::Error(mEGL->getError());
ASSERT(false);
}
}
EGLint SurfaceEGL::getWidth() const
{
EGLint value;
EGLBoolean success = mEGL->querySurface(mSurface, EGL_WIDTH, &value);
ASSERT(success == EGL_TRUE);
return value;
}
EGLint SurfaceEGL::getHeight() const
{
EGLint value;
EGLBoolean success = mEGL->querySurface(mSurface, EGL_HEIGHT, &value);
ASSERT(success == EGL_TRUE);
return value;
}
EGLint SurfaceEGL::isPostSubBufferSupported() const
{
UNIMPLEMENTED();
return 0;
}
EGLint SurfaceEGL::getSwapBehavior() const
{
EGLint value;
EGLBoolean success = mEGL->querySurface(mSurface, EGL_SWAP_BEHAVIOR, &value);
ASSERT(success == EGL_TRUE);
return value;
}
EGLSurface SurfaceEGL::getSurface() const
{
return mSurface;
}
void SurfaceEGL::setTimestampsEnabled(bool enabled)
{
ASSERT(mEGL->hasExtension("EGL_ANDROID_get_frame_timestamps"));
EGLBoolean success =
mEGL->surfaceAttrib(mSurface, EGL_TIMESTAMPS_ANDROID, enabled ? EGL_TRUE : EGL_FALSE);
if (success == EGL_FALSE)
{
ERR() << "eglSurfaceAttribute failed: " << egl::Error(mEGL->getError());
}
}
egl::SupportedCompositorTimings SurfaceEGL::getSupportedCompositorTimings() const
{
ASSERT(mEGL->hasExtension("EGL_ANDROID_get_frame_timestamps"));
egl::SupportedCompositorTimings result;
for (egl::CompositorTiming name : angle::AllEnums<egl::CompositorTiming>())
{
result[name] = mEGL->getCompositorTimingSupportedANDROID(mSurface, egl::ToEGLenum(name));
}
return result;
}
egl::Error SurfaceEGL::getCompositorTiming(EGLint numTimestamps,
const EGLint *names,
EGLnsecsANDROID *values) const
{
ASSERT(mEGL->hasExtension("EGL_ANDROID_get_frame_timestamps"));
egl::Display::GetCurrentThreadUnlockedTailCall()->add(
[egl = mEGL, surface = mSurface, numTimestamps, names, values](void *resultOut) {
EGLBoolean success =
egl->getCompositorTimingANDROID(surface, numTimestamps, names, values);
if (!success)
{
ERR() << "eglGetCompositorTimingANDROID failed: " << egl::Error(egl->getError());
}
*static_cast<EGLBoolean *>(resultOut) = success;
});
return egl::NoError();
}
egl::Error SurfaceEGL::getNextFrameId(EGLuint64KHR *frameId) const
{
ASSERT(mEGL->hasExtension("EGL_ANDROID_get_frame_timestamps"));
EGLBoolean success = mEGL->getNextFrameIdANDROID(mSurface, frameId);
if (success == EGL_FALSE)
{
return egl::Error(mEGL->getError(), "eglGetNextFrameId failed");
}
return egl::NoError();
}
egl::SupportedTimestamps SurfaceEGL::getSupportedTimestamps() const
{
ASSERT(mEGL->hasExtension("EGL_ANDROID_get_frame_timestamps"));
egl::SupportedTimestamps result;
for (egl::Timestamp timestamp : angle::AllEnums<egl::Timestamp>())
{
result[timestamp] =
mEGL->getFrameTimestampSupportedANDROID(mSurface, egl::ToEGLenum(timestamp));
}
return result;
}
egl::Error SurfaceEGL::getFrameTimestamps(EGLuint64KHR frameId,
EGLint numTimestamps,
const EGLint *timestamps,
EGLnsecsANDROID *values) const
{
ASSERT(mEGL->hasExtension("EGL_ANDROID_get_frame_timestamps"));
egl::Display::GetCurrentThreadUnlockedTailCall()->add([egl = mEGL, surface = mSurface, frameId,
numTimestamps, timestamps,
values](void *resultOut) {
EGLBoolean success =
egl->getFrameTimestampsANDROID(surface, frameId, numTimestamps, timestamps, values);
if (!success)
{
// The driver may return EGL_BAD_ACCESS at any time if the requested frame is no longer
// stored.
ERR() << "eglGetFrameTimestampsANDROID failed: " << egl::Error(egl->getError());
}
*static_cast<EGLBoolean *>(resultOut) = success;
});
return egl::NoError();
}
bool SurfaceEGL::isExternal() const
{
return false;
}
} // namespace rx