Commit 6eb7756d by Ian Elliott Committed by Commit Bot

Vulkan: tell ContextVk when swapchain is re-created

For an app that only draws to the swapchain, if the swapchain is recreated with a different rotation (as done by the ANGLE perf tests when switching from Angry Birds 2 to Candy Crush), ContextVk is not informed, and so the new rotation is ignored. Use the subject-observer pattern to set the appropriate dirty bits. Test: run_angle_perftests --gtest_filter=TracePerfTest.Run/vulkan_angry*:*vulkan_candy* --verbose --local-output Bug: angleproject:4910 Bug: b/163126746 Change-Id: Ib5303e9c4095db1b3e736911f483589e40a73d0c Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2341768 Commit-Queue: Ian Elliott <ianelliott@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCourtney Goeltzenleuchter <courtneygo@google.com> Reviewed-by: 's avatarCody Northrop <cnorthrop@google.com>
parent 5e5b7537
...@@ -8184,14 +8184,34 @@ void Context::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMess ...@@ -8184,14 +8184,34 @@ void Context::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMess
break; break;
case kReadFramebufferSubjectIndex: case kReadFramebufferSubjectIndex:
ASSERT(message == angle::SubjectMessage::DirtyBitsFlagged); switch (message)
mState.setReadFramebufferDirty(); {
case angle::SubjectMessage::DirtyBitsFlagged:
mState.setReadFramebufferDirty();
break;
case angle::SubjectMessage::SurfaceChanged:
mState.setReadFramebufferBindingDirty();
break;
default:
UNREACHABLE();
break;
}
break; break;
case kDrawFramebufferSubjectIndex: case kDrawFramebufferSubjectIndex:
ASSERT(message == angle::SubjectMessage::DirtyBitsFlagged); switch (message)
mState.setDrawFramebufferDirty(); {
mStateCache.onDrawFramebufferChange(this); case angle::SubjectMessage::DirtyBitsFlagged:
mState.setDrawFramebufferDirty();
mStateCache.onDrawFramebufferChange(this);
break;
case angle::SubjectMessage::SurfaceChanged:
mState.setDrawFramebufferBindingDirty();
break;
default:
UNREACHABLE();
break;
}
break; break;
case kProgramPipelineSubjectIndex: case kProgramPipelineSubjectIndex:
......
...@@ -1916,6 +1916,13 @@ void Framebuffer::onSubjectStateChange(angle::SubjectIndex index, angle::Subject ...@@ -1916,6 +1916,13 @@ void Framebuffer::onSubjectStateChange(angle::SubjectIndex index, angle::Subject
return; return;
} }
// This can be triggered by external changes to the default framebuffer.
if (message == angle::SubjectMessage::SurfaceChanged)
{
onStateChange(angle::SubjectMessage::SurfaceChanged);
return;
}
// This can be triggered by the GL back-end TextureGL class. // This can be triggered by the GL back-end TextureGL class.
ASSERT(message == angle::SubjectMessage::DirtyBitsFlagged); ASSERT(message == angle::SubjectMessage::DirtyBitsFlagged);
return; return;
......
...@@ -51,6 +51,9 @@ enum class SubjectMessage ...@@ -51,6 +51,9 @@ enum class SubjectMessage
// gl::VertexArray, into gl::Context. Used to track validation. // gl::VertexArray, into gl::Context. Used to track validation.
SubjectMapped, SubjectMapped,
SubjectUnmapped, SubjectUnmapped,
// Indicates an external change to the default framebuffer.
SurfaceChanged,
}; };
// The observing class inherits from this interface class. // The observing class inherits from this interface class.
......
...@@ -758,6 +758,16 @@ class State : angle::NonCopyable ...@@ -758,6 +758,16 @@ class State : angle::NonCopyable
mProvokingVertex = val; mProvokingVertex = val;
} }
ANGLE_INLINE void setReadFramebufferBindingDirty()
{
mDirtyBits.set(State::DIRTY_BIT_READ_FRAMEBUFFER_BINDING);
}
ANGLE_INLINE void setDrawFramebufferBindingDirty()
{
mDirtyBits.set(State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING);
}
using ClipDistanceEnableBits = angle::BitSet32<IMPLEMENTATION_MAX_CLIP_DISTANCES>; using ClipDistanceEnableBits = angle::BitSet32<IMPLEMENTATION_MAX_CLIP_DISTANCES>;
const ClipDistanceEnableBits &getEnabledClipDistances() const { return mClipDistancesEnabled; } const ClipDistanceEnableBits &getEnabledClipDistances() const { return mClipDistancesEnabled; }
void setClipDistanceEnable(int idx, bool enable); void setClipDistanceEnable(int idx, bool enable);
......
...@@ -621,8 +621,19 @@ Error Surface::getFrameTimestamps(EGLuint64KHR frameId, ...@@ -621,8 +621,19 @@ Error Surface::getFrameTimestamps(EGLuint64KHR frameId,
void Surface::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message) void Surface::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message)
{ {
ASSERT(message == angle::SubjectMessage::SubjectChanged && index == kSurfaceImplSubjectIndex); ASSERT(index == kSurfaceImplSubjectIndex);
onStateChange(angle::SubjectMessage::ContentsChanged); switch (message)
{
case angle::SubjectMessage::SubjectChanged:
onStateChange(angle::SubjectMessage::ContentsChanged);
break;
case angle::SubjectMessage::SurfaceChanged:
onStateChange(angle::SubjectMessage::SurfaceChanged);
break;
default:
UNREACHABLE();
break;
}
} }
WindowSurface::WindowSurface(rx::EGLImplFactory *implFactory, WindowSurface::WindowSurface(rx::EGLImplFactory *implFactory,
......
...@@ -765,6 +765,9 @@ angle::Result WindowSurfaceVk::recreateSwapchain(ContextVk *contextVk, ...@@ -765,6 +765,9 @@ angle::Result WindowSurfaceVk::recreateSwapchain(ContextVk *contextVk,
angle::Result result = createSwapChain(contextVk, extents, lastSwapchain); angle::Result result = createSwapChain(contextVk, extents, lastSwapchain);
// Notify the parent classes of the surface's new state.
onStateChange(angle::SubjectMessage::SurfaceChanged);
// If the most recent swapchain was never used, destroy it right now. // If the most recent swapchain was never used, destroy it right now.
if (swapchainToDestroy) if (swapchainToDestroy)
{ {
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "util/EGLWindow.h" #include "util/EGLWindow.h"
#include "util/OSWindow.h" #include "util/OSWindow.h"
#include "util/Timer.h" #include "util/Timer.h"
#include "util/test_utils.h"
using namespace angle; using namespace angle;
...@@ -481,6 +482,137 @@ TEST_P(EGLPreRotationSurfaceTest, OrientedWindowWithDerivativeDraw) ...@@ -481,6 +482,137 @@ TEST_P(EGLPreRotationSurfaceTest, OrientedWindowWithDerivativeDraw)
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
} }
// Android-specific test that changes a window's rotation, which requires ContextVk::syncState() to
// handle the new rotation
TEST_P(EGLPreRotationSurfaceTest, ChangeRotationWithDraw)
{
// This test uses functionality that is only available on Android
ANGLE_SKIP_TEST_IF(isVulkanRenderer() && !IsAndroid());
// To aid in debugging, we want this window visible
setWindowVisible(mOSWindow, true);
initializeDisplay();
initializeSurfaceWithRGBA8888Config();
eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
ASSERT_EGL_SUCCESS();
// Init program
constexpr char kVS[] =
"attribute vec2 position;\n"
"attribute vec2 redGreen;\n"
"varying vec2 v_data;\n"
"void main() {\n"
" gl_Position = vec4(position, 0, 1);\n"
" v_data = redGreen;\n"
"}";
constexpr char kFS[] =
"varying highp vec2 v_data;\n"
"void main() {\n"
" gl_FragColor = vec4(v_data, 0, 1);\n"
"}";
GLuint program = CompileProgram(kVS, kFS);
ASSERT_NE(0u, program);
glUseProgram(program);
GLint positionLocation = glGetAttribLocation(program, "position");
ASSERT_NE(-1, positionLocation);
GLint redGreenLocation = glGetAttribLocation(program, "redGreen");
ASSERT_NE(-1, redGreenLocation);
GLuint indexBuffer;
glGenBuffers(1, &indexBuffer);
GLuint vertexArray;
glGenVertexArrays(1, &vertexArray);
std::vector<GLuint> vertexBuffers(2);
glGenBuffers(2, &vertexBuffers[0]);
glBindVertexArray(vertexArray);
std::vector<GLushort> indices = {0, 1, 2, 2, 3, 0};
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * indices.size(), &indices[0],
GL_STATIC_DRAW);
std::vector<GLfloat> positionData = {// quad vertices
-1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f};
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffers[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * positionData.size(), &positionData[0],
GL_STATIC_DRAW);
glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr);
glEnableVertexAttribArray(positionLocation);
std::vector<GLfloat> redGreenData = {// green(0,1), black(0,0), red(1,0), yellow(1,1)
0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f};
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffers[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * redGreenData.size(), &redGreenData[0],
GL_STATIC_DRAW);
glVertexAttribPointer(redGreenLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr);
glEnableVertexAttribArray(redGreenLocation);
ASSERT_GL_NO_ERROR();
// Change the rotation back and forth between landscape and portrait, and make sure that the
// drawing and reading happen consistently with the desired rotation.
for (int i = 0; i < 3; i++)
{
bool landscape;
EGLint actualWidth = 0;
EGLint actualHeight = 0;
EGLint desiredWidth = 0;
EGLint desiredHeight = 0;
if ((i % 2) == 0)
{
landscape = true;
desiredWidth = 300;
desiredHeight = 200;
}
else
{
landscape = false;
desiredWidth = 200;
desiredHeight = 300;
}
mOSWindow->resize(desiredWidth, desiredHeight);
// setOrientation() uses a reverse-JNI call, which sends data to other parts of Android.
// Sometime later (i.e. asynchronously), the window is updated. Sleep a little here, and
// then allow for multiple eglSwapBuffers calls to eventually see the new rotation.
mOSWindow->setOrientation(desiredWidth, desiredHeight);
angle::Sleep(1000);
eglSwapBuffers(mDisplay, mWindowSurface);
ASSERT_EGL_SUCCESS();
while ((actualWidth != desiredWidth) && (actualHeight != desiredHeight))
{
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
if (landscape)
{
EXPECT_PIXEL_COLOR_EQ(mSize - 1, 0, GLColor::red);
}
else
{
EXPECT_PIXEL_COLOR_EQ(0, mSize - 1, GLColor::green);
}
ASSERT_GL_NO_ERROR();
eglSwapBuffers(mDisplay, mWindowSurface);
ASSERT_EGL_SUCCESS();
eglQuerySurface(mDisplay, mWindowSurface, EGL_HEIGHT, &actualHeight);
eglQuerySurface(mDisplay, mWindowSurface, EGL_WIDTH, &actualWidth);
}
}
}
// A slight variation of EGLPreRotationSurfaceTest, where the initial window size is 400x300, yet // A slight variation of EGLPreRotationSurfaceTest, where the initial window size is 400x300, yet
// the drawing is still 256x256. In addition, gl_FragCoord is used in a "clever" way, as the color // the drawing is still 256x256. In addition, gl_FragCoord is used in a "clever" way, as the color
// of the 256x256 drawing area, which reproduces an interesting pre-rotation case from the // of the 256x256 drawing area, which reproduces an interesting pre-rotation case from the
......
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