Commit e488d8b8 by Ian Elliott Committed by Commit Bot

Vulkan: Implement Android pre-rotation

As an Android GLES driver on top of Vulkan, ANGLE must pre-rotate rendering on behalf of the application. This involves modifying the vertex shader to multiply gl_Position with a mat2 "rotation matrix". Not doing so means that SurfaceFlinger (SF) will perform a costly rotation blit before presenting every image. Setting WindowSurfaceVk::mPreTransform to mCurrentTransform tells SF to not do the blit. When the surface is rotated 90 or 270 degrees, the width and height must be swapped for: - The swapchain images, and for any depth, stencil, and/or multisample attachments used with the swapchain image. - The viewport, scissor, and render area. Because the Metal back-end shares the TranslatorVulkan, it will define the same preRotation (mat2) DriverUniform that is used for Vulkan. Bug: angleproject:3502 Change-Id: I968dbe8869ba0f50de18dd41f1195e847c06b545 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2038272 Commit-Queue: Ian Elliott <ianelliott@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent ba8ef68c
......@@ -244,6 +244,14 @@ struct FeaturesVk : FeatureSetBase
"allocate_non_zero_memory", FeatureCategory::VulkanFeatures,
"Fill new allocations with non-zero values to flush out errors.", &members,
"http://anglebug.com/4384"};
// Android needs to pre-rotate surfaces that are not oriented per the native device's
// orientation (e.g. a landscape application on a Pixel phone). This feature works for
// full-screen applications. http://anglebug.com/3502
Feature enablePreRotateSurfaces = {"enable_pre_rotation_surfaces",
FeatureCategory::VulkanFeatures,
"Enable Android pre-rotation for landscape applications",
&members, "http://anglebug.com/3502"};
};
inline FeaturesVk::FeaturesVk() = default;
......
......@@ -180,11 +180,12 @@ constexpr const char kXfbVerticesPerDraw[] = "xfbVerticesPerDraw";
constexpr const char kXfbBufferOffsets[] = "xfbBufferOffsets";
constexpr const char kAcbBufferOffsets[] = "acbBufferOffsets";
constexpr const char kDepthRange[] = "depthRange";
constexpr const char kPreRotation[] = "preRotation";
constexpr size_t kNumGraphicsDriverUniforms = 9;
constexpr size_t kNumGraphicsDriverUniforms = 10;
constexpr std::array<const char *, kNumGraphicsDriverUniforms> kGraphicsDriverUniformNames = {
{kViewport, kHalfRenderAreaHeight, kViewportYScale, kNegViewportYScale, kXfbActiveUnpaused,
kXfbVerticesPerDraw, kXfbBufferOffsets, kAcbBufferOffsets, kDepthRange}};
kXfbVerticesPerDraw, kXfbBufferOffsets, kAcbBufferOffsets, kDepthRange, kPreRotation}};
constexpr size_t kNumComputeDriverUniforms = 1;
constexpr std::array<const char *, kNumComputeDriverUniforms> kComputeDriverUniformNames = {
......@@ -332,6 +333,35 @@ ANGLE_NO_DISCARD bool AppendVertexShaderDepthCorrectionToMain(TCompiler *compile
return RunAtTheEndOfShader(compiler, root, assignment, symbolTable);
}
// This operation performs Android pre-rotation and y-flip. For Android (and potentially other
// platforms), the device may rotate, such that the orientation of the application is rotated
// relative to the native orientation of the device. This is corrected in part by multiplying
// gl_Position by a mat2.
// The equations reduce to an expression:
//
// gl_Position.xy = gl_Position.xy * preRotation
ANGLE_NO_DISCARD bool AppendPreRotation(TCompiler *compiler,
TIntermBlock *root,
TSymbolTable *symbolTable,
const TVariable *driverUniforms)
{
TIntermBinary *preRotationRef = CreateDriverUniformRef(driverUniforms, kPreRotation);
TIntermSymbol *glPos = new TIntermSymbol(BuiltInVariable::gl_Position());
TVector<int> swizzleOffsetXY = {0, 1};
TIntermSwizzle *glPosXY = new TIntermSwizzle(glPos, swizzleOffsetXY);
// Create the expression "(gl_Position.xy * preRotation)"
TIntermBinary *zRotated =
new TIntermBinary(EOpMatrixTimesVector, preRotationRef->deepCopy(), glPosXY->deepCopy());
// Create the assignment "gl_Position.xy = (gl_Position.xy * preRotation)"
TIntermBinary *assignment =
new TIntermBinary(TOperator::EOpAssign, glPosXY->deepCopy(), zRotated);
// Append the assignment as a statement at the end of the shader.
return RunAtTheEndOfShader(compiler, root, assignment, symbolTable);
}
ANGLE_NO_DISCARD bool AppendVertexShaderTransformFeedbackOutputToMain(TCompiler *compiler,
TIntermBlock *root,
TSymbolTable *symbolTable)
......@@ -390,6 +420,7 @@ const TVariable *AddGraphicsDriverUniformsToShader(TIntermBlock *root, TSymbolTa
new TType(EbtInt, 4),
new TType(EbtUInt, 4),
emulatedDepthRangeType,
new TType(EbtFloat, 2, 2),
}};
for (size_t uniformIndex = 0; uniformIndex < kNumGraphicsDriverUniforms; ++uniformIndex)
......@@ -976,6 +1007,10 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
{
return false;
}
if (!AppendPreRotation(this, root, &getSymbolTable(), driverUniforms))
{
return false;
}
}
else if (getShaderType() == GL_GEOMETRY_SHADER)
{
......
......@@ -393,6 +393,10 @@ class ContextMtl : public ContextImpl, public mtl::Context
// We'll use x, y, z, w for near / far / diff / zscale respectively.
float depthRange[4];
// Used to pre-rotate gl_Position for Vulkan swapchain images on Android (a mat2, which is
// padded to the size of two vec4's).
float preRotation[8];
};
struct DefaultAttribute
......
......@@ -1607,6 +1607,16 @@ angle::Result ContextMtl::handleDirtyDriverUniforms(const gl::Context *context)
mDriverUniforms.depthRange[2] = depthRangeDiff;
mDriverUniforms.depthRange[3] = NeedToInvertDepthRange(depthRangeNear, depthRangeFar) ? -1 : 1;
// Fill in a mat2 identity matrix, plus padding
mDriverUniforms.preRotation[0] = 1.0f;
mDriverUniforms.preRotation[1] = 0.0f;
mDriverUniforms.preRotation[2] = 0.0f;
mDriverUniforms.preRotation[3] = 0.0f;
mDriverUniforms.preRotation[4] = 0.0f;
mDriverUniforms.preRotation[5] = 1.0f;
mDriverUniforms.preRotation[6] = 0.0f;
mDriverUniforms.preRotation[7] = 0.0f;
ASSERT(mRenderEncoder.valid());
mRenderEncoder.setFragmentData(mDriverUniforms, mtl::kDriverUniformsBindingIndex);
mRenderEncoder.setVertexData(mDriverUniforms, mtl::kDriverUniformsBindingIndex);
......
......@@ -75,6 +75,10 @@ struct GraphicsDriverUniforms
// We'll use x, y, z for near / far / diff respectively.
std::array<float, 4> depthRange;
// Used to pre-rotate gl_Position for swapchain images on Android (a mat2, which is padded to
// the size of two vec4's).
std::array<float, 8> preRotation;
};
struct ComputeDriverUniforms
......@@ -198,6 +202,71 @@ char GetStoreOpShorthand(uint32_t storeOp)
return 'D';
}
}
// When an Android surface is rotated differently than the device's native orientation, ANGLE must
// rotate gl_Position in the vertex shader. The following are the rotation matrices used.
//
// Note: these are mat2's that are appropriately padded (4 floats per row).
using PreRotationMatrixValues = std::array<float, 8>;
constexpr angle::PackedEnumMap<rx::SurfaceRotationType,
PreRotationMatrixValues,
angle::EnumSize<rx::SurfaceRotationType>()>
kPreRotationMatrices = {
{{rx::SurfaceRotationType::Identity, {{1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}}},
{rx::SurfaceRotationType::Rotated90Degrees,
{{0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}}},
{rx::SurfaceRotationType::Rotated180Degrees,
{{-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}}},
{rx::SurfaceRotationType::Rotated270Degrees,
{{0.0f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f}}},
{rx::SurfaceRotationType::FlippedIdentity,
{{1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}}},
{rx::SurfaceRotationType::FlippedRotated90Degrees,
{{0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f}}},
{rx::SurfaceRotationType::FlippedRotated180Degrees,
{{-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}}},
{rx::SurfaceRotationType::FlippedRotated270Degrees,
{{0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}}}}};
bool IsRotatedAspectRatio(SurfaceRotationType rotation)
{
return ((rotation == SurfaceRotationType::Rotated90Degrees) ||
(rotation == SurfaceRotationType::Rotated270Degrees) ||
(rotation == SurfaceRotationType::FlippedRotated90Degrees) ||
(rotation == SurfaceRotationType::FlippedRotated270Degrees));
}
SurfaceRotationType DetermineSurfaceRotation(gl::Framebuffer *framebuffer,
WindowSurfaceVk *windowSurface)
{
if (windowSurface && framebuffer->isDefault())
{
switch (windowSurface->getPreTransform())
{
case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
// Do not rotate gl_Position (surface matches the device's orientation):
return SurfaceRotationType::Identity;
case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
// Rotate gl_Position 90 degrees:
return SurfaceRotationType::Rotated90Degrees;
case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
// Rotate gl_Position 180 degrees:
return SurfaceRotationType::Rotated180Degrees;
case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
// Rotate gl_Position 270 degrees:
return SurfaceRotationType::Rotated180Degrees;
default:
UNREACHABLE();
return SurfaceRotationType::Identity;
}
}
else
{
// Do not rotate gl_Position (offscreen framebuffer):
return SurfaceRotationType::Identity;
}
}
} // anonymous namespace
ContextVk::DriverUniformsDescriptorSet::DriverUniformsDescriptorSet()
......@@ -517,6 +586,8 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk
mCurrentComputePipeline(nullptr),
mCurrentDrawMode(gl::PrimitiveMode::InvalidEnum),
mCurrentWindowSurface(nullptr),
mCurrentRotationDrawFramebuffer(SurfaceRotationType::Identity),
mCurrentRotationReadFramebuffer(SurfaceRotationType::Identity),
mVertexArray(nullptr),
mDrawFramebuffer(nullptr),
mProgram(nullptr),
......@@ -2287,6 +2358,15 @@ bool ContextVk::isViewportFlipEnabledForReadFBO() const
return mFlipViewportForReadFramebuffer;
}
bool ContextVk::isRotatedAspectRatioForDrawFBO() const
{
return IsRotatedAspectRatio(mCurrentRotationDrawFramebuffer);
}
bool ContextVk::isRotatedAspectRatioForReadFBO() const
{
return IsRotatedAspectRatio(mCurrentRotationReadFramebuffer);
}
void ContextVk::updateColorMask(const gl::BlendState &blendState)
{
mClearColorMask =
......@@ -2354,11 +2434,21 @@ void ContextVk::updateViewport(FramebufferVk *framebufferVk,
correctedHeight = viewportBoundsRangeHigh - correctedY;
}
gl::Box framebufferDimensions = framebufferVk->getState().getDimensions();
if (isRotatedAspectRatioForDrawFBO())
{
// The surface is rotated 90/270 degrees. This changes the aspect ratio of the surface.
// Swap the width and height of the viewport we calculate, and the framebuffer height that
// we use to calculate it with.
// TODO(ianelliott): handle small viewport/scissor cases. http://anglebug.com/4431
std::swap(correctedWidth, correctedHeight);
std::swap(framebufferDimensions.width, framebufferDimensions.height);
}
gl::Rectangle correctedRect =
gl::Rectangle(correctedX, correctedY, correctedWidth, correctedHeight);
gl_vk::GetViewport(correctedRect, nearPlane, farPlane, invertViewport,
framebufferVk->getState().getDimensions().height, &vkViewport);
framebufferDimensions.height, &vkViewport);
mGraphicsPipelineDesc->updateViewport(&mGraphicsPipelineTransition, vkViewport);
invalidateGraphicsDriverUniforms();
}
......@@ -2383,6 +2473,13 @@ angle::Result ContextVk::updateScissor(const gl::State &glState)
{
scissoredArea.y = renderArea.height - scissoredArea.y - scissoredArea.height;
}
if (isRotatedAspectRatioForDrawFBO())
{
// The surface is rotated 90/270 degrees. This changes the aspect ratio of the surface.
// Swap the width and height of the scissor we use.
// TODO(ianelliott): handle small viewport/scissor cases. http://anglebug.com/4431
std::swap(scissoredArea.width, scissoredArea.height);
}
mGraphicsPipelineDesc->updateScissor(&mGraphicsPipelineTransition,
gl_vk::GetRect(scissoredArea));
......@@ -2588,6 +2685,7 @@ angle::Result ContextVk::syncState(const gl::Context *context,
break;
case gl::State::DIRTY_BIT_READ_FRAMEBUFFER_BINDING:
updateFlipViewportReadFramebuffer(context->getState());
updateSurfaceRotationReadFramebuffer(glState);
break;
case gl::State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING:
{
......@@ -2601,6 +2699,7 @@ angle::Result ContextVk::syncState(const gl::Context *context,
gl::Framebuffer *drawFramebuffer = glState.getDrawFramebuffer();
mDrawFramebuffer = vk::GetImpl(drawFramebuffer);
updateFlipViewportDrawFramebuffer(glState);
updateSurfaceRotationDrawFramebuffer(glState);
updateViewport(mDrawFramebuffer, glState.getViewport(), glState.getNearPlane(),
glState.getFarPlane(), isViewportFlipEnabledForDrawFBO());
updateColorMask(glState.getBlendState());
......@@ -2770,6 +2869,8 @@ angle::Result ContextVk::onMakeCurrent(const gl::Context *context)
const gl::State &glState = context->getState();
updateFlipViewportDrawFramebuffer(glState);
updateFlipViewportReadFramebuffer(glState);
updateSurfaceRotationDrawFramebuffer(glState);
updateSurfaceRotationReadFramebuffer(glState);
invalidateDriverUniforms();
return angle::Result::Continue;
......@@ -2796,6 +2897,20 @@ void ContextVk::updateFlipViewportReadFramebuffer(const gl::State &glState)
readFramebuffer->isDefault() && mRenderer->getFeatures().flipViewportY.enabled;
}
void ContextVk::updateSurfaceRotationDrawFramebuffer(const gl::State &glState)
{
gl::Framebuffer *drawFramebuffer = glState.getDrawFramebuffer();
mCurrentRotationDrawFramebuffer =
DetermineSurfaceRotation(drawFramebuffer, mCurrentWindowSurface);
}
void ContextVk::updateSurfaceRotationReadFramebuffer(const gl::State &glState)
{
gl::Framebuffer *readFramebuffer = glState.getReadFramebuffer();
mCurrentRotationDrawFramebuffer =
DetermineSurfaceRotation(readFramebuffer, mCurrentWindowSurface);
}
gl::Caps ContextVk::getNativeCaps() const
{
return mRenderer->getNativeCaps();
......@@ -3177,9 +3292,17 @@ angle::Result ContextVk::handleDirtyGraphicsDriverUniforms(const gl::Context *co
&mDriverUniforms[PipelineType::Graphics], &buffer, &ptr,
&newBuffer));
const gl::Rectangle &glViewport = mState.getViewport();
gl::Rectangle glViewport = mState.getViewport();
float halfRenderAreaHeight =
static_cast<float>(mDrawFramebuffer->getState().getDimensions().height) * 0.5f;
if (isRotatedAspectRatioForDrawFBO())
{
// The surface is rotated 90/270 degrees. This changes the aspect ratio of the surface.
// TODO(ianelliott): handle small viewport/scissor cases. http://anglebug.com/4431
std::swap(glViewport.width, glViewport.height);
halfRenderAreaHeight =
static_cast<float>(mDrawFramebuffer->getState().getDimensions().width) * 0.5f;
}
float scaleY = isViewportFlipEnabledForDrawFBO() ? -1.0f : 1.0f;
uint32_t xfbActiveUnpaused = mState.isTransformFeedbackActiveUnpaused();
......@@ -3201,7 +3324,10 @@ angle::Result ContextVk::handleDirtyGraphicsDriverUniforms(const gl::Context *co
{},
{},
{},
{depthRangeNear, depthRangeFar, depthRangeDiff, 0.0f}};
{depthRangeNear, depthRangeFar, depthRangeDiff, 0.0f},
{}};
memcpy(&driverUniforms->preRotation, &kPreRotationMatrices[mCurrentRotationDrawFramebuffer],
sizeof(PreRotationMatrixValues));
if (xfbActiveUnpaused)
{
......@@ -3870,7 +3996,17 @@ angle::Result ContextVk::flushAndBeginRenderPass(
{
vk::PrimaryCommandBuffer *primary;
ANGLE_TRY(flushAndGetPrimaryCommandBuffer(&primary));
mRenderPassCommands.beginRenderPass(framebuffer, renderArea, renderPassDesc,
gl::Rectangle rotatedRenderArea = renderArea;
if (isRotatedAspectRatioForDrawFBO())
{
// The surface is rotated 90/270 degrees. This changes the aspect ratio of
// the surface. Swap the width and height of the renderArea.
// TODO(ianelliott): handle small viewport/scissor cases. http://anglebug.com/4431
std::swap(rotatedRenderArea.width, rotatedRenderArea.height);
}
mRenderPassCommands.beginRenderPass(framebuffer, rotatedRenderArea, renderPassDesc,
renderPassAttachmentOps, clearValues, commandBufferOut);
return angle::Result::Continue;
}
......
......@@ -29,6 +29,23 @@ namespace rx
class RendererVk;
class WindowSurfaceVk;
// The possible rotations of the surface/draw framebuffer, used for pre-rotating gl_Position
// in the vertex shader.
enum class SurfaceRotationType
{
Identity,
Rotated90Degrees,
Rotated180Degrees,
Rotated270Degrees,
FlippedIdentity,
FlippedRotated90Degrees,
FlippedRotated180Degrees,
FlippedRotated270Degrees,
InvalidEnum,
EnumCount = InvalidEnum,
};
struct CommandBatch final : angle::NonCopyable
{
CommandBatch();
......@@ -345,6 +362,11 @@ class ContextVk : public ContextImpl, public vk::Context
bool isViewportFlipEnabledForDrawFBO() const;
bool isViewportFlipEnabledForReadFBO() const;
// When the device/surface is rotated such that the surface's aspect ratio is different than
// the native device (e.g. 90 degrees), the width and height of the viewport, scissor, and
// render area must be swapped.
bool isRotatedAspectRatioForDrawFBO() const;
bool isRotatedAspectRatioForReadFBO() const;
// State sync with dirty bits.
angle::Result syncState(const gl::Context *context,
......@@ -775,6 +797,8 @@ class ContextVk : public ContextImpl, public vk::Context
void updateDepthRange(float nearPlane, float farPlane);
void updateFlipViewportDrawFramebuffer(const gl::State &glState);
void updateFlipViewportReadFramebuffer(const gl::State &glState);
void updateSurfaceRotationDrawFramebuffer(const gl::State &glState);
void updateSurfaceRotationReadFramebuffer(const gl::State &glState);
angle::Result updateActiveTextures(const gl::Context *context);
angle::Result updateActiveImages(const gl::Context *context,
......@@ -895,6 +919,10 @@ class ContextVk : public ContextImpl, public vk::Context
gl::PrimitiveMode mCurrentDrawMode;
WindowSurfaceVk *mCurrentWindowSurface;
// Records the current rotation of the surface (draw/read) framebuffer, derived from
// mCurrentWindowSurface->getPreTransform().
SurfaceRotationType mCurrentRotationDrawFramebuffer;
SurfaceRotationType mCurrentRotationReadFramebuffer;
// Keep a cached pipeline description structure that can be used to query the pipeline cache.
// Kept in a pointer so allocations can be aligned, and structs can be portably packed.
......
......@@ -1646,6 +1646,9 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev
(&mFeatures), supportsExternalMemoryHost,
ExtensionFound(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME, deviceExtensionNames));
// Pre-rotation support is not fully ready to be enabled.
ANGLE_FEATURE_CONDITION((&mFeatures), enablePreRotateSurfaces, false);
angle::PlatformMethods *platform = ANGLEPlatformCurrent();
platform->overrideFeaturesVk(platform, &mFeatures);
......
......@@ -95,6 +95,19 @@ constexpr VkImageUsageFlags kSurfaceVKColorImageUsageFlags =
constexpr VkImageUsageFlags kSurfaceVKDepthStencilImageUsageFlags =
kSurfaceVKImageUsageFlags | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
// If the device is rotated with any of the following transform flags, the swapchain width and
// height must be swapped (e.g. make a landscape window portrait). This must also be done for all
// attachments used with the swapchain (i.e. depth, stencil, and multisample buffers).
constexpr VkSurfaceTransformFlagsKHR k90DegreeRotationVariants =
VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR |
VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR |
VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR;
bool Is90DegreeRotation(VkSurfaceTransformFlagsKHR transform)
{
return ((transform & k90DegreeRotationVariants) != 0);
}
} // namespace
SurfaceVk::SurfaceVk(const egl::SurfaceState &surfaceState) : SurfaceImpl(surfaceState) {}
......@@ -554,6 +567,27 @@ angle::Result WindowSurfaceVk::initializeImpl(DisplayVk *displayVk)
gl::Extents extents(static_cast<int>(width), static_cast<int>(height), 1);
if (renderer->getFeatures().enablePreRotateSurfaces.enabled)
{
// Use the surface's transform. For many platforms, this will always be identity (ANGLE
// does not need to do any pre-rotation). However, when mSurfaceCaps.currentTransform is
// not identity, the device has been rotated away from its natural orientation. In such a
// case, ANGLE must rotate all rendering in order to avoid the compositor
// (e.g. SurfaceFlinger on Android) performing an additional rotation blit. In addition,
// ANGLE must create the swapchain with VkSwapchainCreateInfoKHR::preTransform set to the
// value of mSurfaceCaps.currentTransform.
mPreTransform = mSurfaceCaps.currentTransform;
}
else
{
// Default to identity transform.
mPreTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
if ((mSurfaceCaps.supportedTransforms & mPreTransform) == 0)
{
mPreTransform = mSurfaceCaps.currentTransform;
}
}
uint32_t presentModeCount = 0;
ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, mSurface,
&presentModeCount, nullptr));
......@@ -567,13 +601,6 @@ angle::Result WindowSurfaceVk::initializeImpl(DisplayVk *displayVk)
// will get clamped to the min/max values specified at display creation time.
setSwapInterval(renderer->getFeatures().disableFifoPresentMode.enabled ? 0 : 1);
// Default to identity transform.
mPreTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
if ((mSurfaceCaps.supportedTransforms & mPreTransform) == 0)
{
mPreTransform = mSurfaceCaps.currentTransform;
}
uint32_t surfaceFormatCount = 0;
ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, mSurface,
&surfaceFormatCount, nullptr));
......@@ -793,6 +820,19 @@ angle::Result WindowSurfaceVk::createSwapChain(vk::Context *context,
const vk::Format &format = renderer->getFormat(mState.config->renderTargetFormat);
VkFormat nativeFormat = format.vkImageFormat;
gl::Extents rotatedExtents = extents;
if (Is90DegreeRotation(mPreTransform))
{
// The Surface is oriented such that its aspect ratio no longer matches that of the
// device. In this case, the width and height of the swapchain images must be swapped to
// match the device's native orientation. This must also be done for other attachments
// used with the swapchain (e.g. depth buffer). The width and height of the viewport,
// scissor, and render-pass render area must also be swapped. Then, when ANGLE rotates
// gl_Position in the vertex shader, the rendering will look the same as if no
// pre-rotation had been done.
std::swap(rotatedExtents.width, rotatedExtents.height);
}
// We need transfer src for reading back from the backbuffer.
VkImageUsageFlags imageUsageFlags = kSurfaceVKColorImageUsageFlags;
......@@ -813,8 +853,8 @@ angle::Result WindowSurfaceVk::createSwapChain(vk::Context *context,
swapchainInfo.imageColorSpace = MapEglColorSpaceToVkColorSpace(
static_cast<EGLenum>(mState.attributes.get(EGL_GL_COLORSPACE, EGL_NONE)));
// Note: Vulkan doesn't allow 0-width/height swapchains.
swapchainInfo.imageExtent.width = std::max(extents.width, 1);
swapchainInfo.imageExtent.height = std::max(extents.height, 1);
swapchainInfo.imageExtent.width = std::max(rotatedExtents.width, 1);
swapchainInfo.imageExtent.height = std::max(rotatedExtents.height, 1);
swapchainInfo.imageArrayLayers = 1;
swapchainInfo.imageUsage = imageUsageFlags;
swapchainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
......@@ -845,7 +885,7 @@ angle::Result WindowSurfaceVk::createSwapChain(vk::Context *context,
ANGLE_VK_CHECK(context, samples > 0, VK_ERROR_INITIALIZATION_FAILED);
VkExtent3D vkExtents;
gl_vk::GetExtent(extents, &vkExtents);
gl_vk::GetExtent(rotatedExtents, &vkExtents);
if (samples > 1)
{
......@@ -926,12 +966,17 @@ angle::Result WindowSurfaceVk::checkForOutOfDateSwapchain(ContextVk *contextVk,
// If window size has changed, check with surface capabilities. It has been observed on
// Android that `getCurrentWindowSize()` returns 1920x1080 for example, while surface
// capabilities returns the size the surface was created with.
if (currentExtents != swapchainExtents)
const VkPhysicalDevice &physicalDevice = contextVk->getRenderer()->getPhysicalDevice();
ANGLE_VK_TRY(contextVk, vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, mSurface,
&mSurfaceCaps));
if (contextVk->getFeatures().enablePreRotateSurfaces.enabled)
{
const VkPhysicalDevice &physicalDevice = contextVk->getRenderer()->getPhysicalDevice();
ANGLE_VK_TRY(contextVk, vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
physicalDevice, mSurface, &mSurfaceCaps));
// Update the surface's transform, which can change even if the window size does not.
mPreTransform = mSurfaceCaps.currentTransform;
}
if (currentExtents != swapchainExtents)
{
uint32_t width = mSurfaceCaps.currentExtent.width;
uint32_t height = mSurfaceCaps.currentExtent.height;
......@@ -1179,13 +1224,24 @@ angle::Result WindowSurfaceVk::present(ContextVk *contextVk,
// If OUT_OF_DATE is returned, it's ok, we just need to recreate the swapchain before
// continuing.
// If VK_SUBOPTIMAL_KHR is returned it's because the device orientation changed and we should
// recreate the swapchain with a new window orientation. We aren't quite ready for that so just
// ignore for now.
// TODO: Check for preRotation: http://anglebug.com/3502
*presentOutOfDate = result == VK_ERROR_OUT_OF_DATE_KHR;
if (!*presentOutOfDate && result != VK_SUBOPTIMAL_KHR)
// recreate the swapchain with a new window orientation.
if (contextVk->getFeatures().enablePreRotateSurfaces.enabled)
{
ANGLE_VK_TRY(contextVk, result);
// Also check for VK_SUBOPTIMAL_KHR.
*presentOutOfDate = ((result == VK_ERROR_OUT_OF_DATE_KHR) || (result == VK_SUBOPTIMAL_KHR));
if (!*presentOutOfDate)
{
ANGLE_VK_TRY(contextVk, result);
}
}
else
{
// We aren't quite ready for that so just ignore for now.
*presentOutOfDate = result == VK_ERROR_OUT_OF_DATE_KHR;
if (!*presentOutOfDate && result != VK_SUBOPTIMAL_KHR)
{
ANGLE_VK_TRY(contextVk, result);
}
}
return angle::Result::Continue;
......@@ -1399,7 +1455,11 @@ angle::Result WindowSurfaceVk::getCurrentFramebuffer(ContextVk *contextVk,
framebufferInfo.pAttachments = imageViews.data();
framebufferInfo.width = static_cast<uint32_t>(extents.width);
framebufferInfo.height = static_cast<uint32_t>(extents.height);
framebufferInfo.layers = 1;
if (Is90DegreeRotation(mPreTransform))
{
std::swap(framebufferInfo.width, framebufferInfo.height);
}
framebufferInfo.layers = 1;
if (isMultiSampled())
{
......
......@@ -224,6 +224,8 @@ class WindowSurfaceVk : public SurfaceVk
vk::Semaphore getAcquireImageSemaphore();
VkSurfaceTransformFlagBitsKHR getPreTransform() { return mPreTransform; }
protected:
angle::Result swapImpl(const gl::Context *context,
EGLint *rects,
......
......@@ -1162,6 +1162,12 @@ angle::Result UtilsVk::startRenderPass(ContextVk *contextVk,
framebufferInfo.width = renderArea.x + renderArea.width;
framebufferInfo.height = renderArea.y + renderArea.height;
framebufferInfo.layers = 1;
if (contextVk->isRotatedAspectRatioForDrawFBO())
{
// The surface is rotated 90/270 degrees. This changes the aspect ratio of
// the surface. Swap the width and height of the framebuffer.
std::swap(framebufferInfo.width, framebufferInfo.height);
}
vk::Framebuffer framebuffer;
ANGLE_VK_TRY(contextVk, framebuffer.init(contextVk->getDevice(), framebufferInfo));
......
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