Vulkan: Support y-flip with no driver support.

We can reuse the surface rotation matrix code to do the y-flip.
This requires the SPIR-V transformation support. Because not
all rotations are encoded into the table we can only support
rotation with the driver support for y-flip (currently).

Includes some very minimal regression testing. This work is
targeted towards supporting vk-portability implementations
which are not as up-to-date with Vulkan features.

Bug: angleproject:5596
Change-Id: I270fa1efc03267551d28df33ddac9972e1343d60
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2665892
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Ian Elliott <ianelliott@google.com>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
This commit is contained in:
Jamie Madill
2021-02-01 17:18:18 -05:00
committed by Commit Bot
parent 4e1b46d33e
commit a8a2a71b3a
13 changed files with 84 additions and 18 deletions

View File

@@ -467,6 +467,10 @@ struct FeaturesVk : FeatureSetBase
"emulateR32fImageAtomicExchange", FeatureCategory::VulkanWorkarounds,
"Emulate r32f images with r32ui to support imageAtomicExchange.", &members,
"http://anglebug.com/5535"};
Feature supportsNegativeViewport = {
"supportsNegativeViewport", FeatureCategory::VulkanFeatures,
"The driver supports inverting the viewport with a negative height.", &members};
};
inline FeaturesVk::FeaturesVk() = default;

View File

@@ -1807,12 +1807,28 @@ void SpirvTransformer::preRotateXY(spirv::IdRef xId,
switch (mOptions.preRotation)
{
case SurfaceRotation::Identity:
case SurfaceRotation::FlippedIdentity:
// [ 1 0] [x]
// [ 0 1] * [y]
*rotatedXIdOut = xId;
*rotatedYIdOut = yId;
break;
case SurfaceRotation::FlippedIdentity:
if (mOptions.negativeViewportSupported)
{
// [ 1 0] [x]
// [ 0 1] * [y]
*rotatedXIdOut = xId;
*rotatedYIdOut = yId;
}
else
{
// [ 1 0] [x]
// [ 0 -1] * [y]
*rotatedXIdOut = xId;
*rotatedYIdOut = getNewId();
spirv::WriteFNegate(mSpirvBlobOut, mFloatId, *rotatedYIdOut, yId);
}
break;
case SurfaceRotation::Rotated90Degrees:
case SurfaceRotation::FlippedRotated90Degrees:
// [ 0 1] [x]

View File

@@ -60,6 +60,7 @@ struct GlslangSpirvOptions
{
gl::ShaderType shaderType = gl::ShaderType::InvalidEnum;
SurfaceRotation preRotation = SurfaceRotation::Identity;
bool negativeViewportSupported = false;
bool transformPositionToVulkanClipSpace = false;
bool removeEarlyFragmentTestsOptimization = false;
bool removeDebugInfo = false;

View File

@@ -2634,8 +2634,7 @@ gl::Rectangle ContextVk::getCorrectedViewport(const gl::Rectangle &viewport) con
void ContextVk::updateViewport(FramebufferVk *framebufferVk,
const gl::Rectangle &viewport,
float nearPlane,
float farPlane,
bool invertViewport)
float farPlane)
{
gl::Box fbDimensions = framebufferVk->getState().getDimensions();
@@ -2644,6 +2643,9 @@ void ContextVk::updateViewport(FramebufferVk *framebufferVk,
RotateRectangle(getRotationDrawFramebuffer(), false, fbDimensions.width, fbDimensions.height,
correctedRect, &rotatedRect);
bool invertViewport =
isViewportFlipEnabledForDrawFBO() && getFeatures().supportsNegativeViewport.enabled;
VkViewport vkViewport;
gl_vk::GetViewport(
rotatedRect, nearPlane, farPlane, invertViewport,
@@ -2801,7 +2803,7 @@ angle::Result ContextVk::syncState(const gl::Context *context,
{
FramebufferVk *framebufferVk = vk::GetImpl(glState.getDrawFramebuffer());
updateViewport(framebufferVk, glState.getViewport(), glState.getNearPlane(),
glState.getFarPlane(), isViewportFlipEnabledForDrawFBO());
glState.getFarPlane());
// Update the scissor, which will be constrained to the viewport
updateScissor(glState);
break;
@@ -2984,7 +2986,7 @@ angle::Result ContextVk::syncState(const gl::Context *context,
SpecConstUsageBits usageBits = getCurrentProgramSpecConstUsageBits();
updateGraphicsPipelineDescWithSpecConstUsageBits(usageBits);
updateViewport(mDrawFramebuffer, glState.getViewport(), glState.getNearPlane(),
glState.getFarPlane(), isViewportFlipEnabledForDrawFBO());
glState.getFarPlane());
updateColorMasks(glState.getBlendStateExt());
updateRasterizationSamples(mDrawFramebuffer->getSamples());
@@ -3097,8 +3099,7 @@ angle::Result ContextVk::syncState(const gl::Context *context,
case gl::State::ExtendedDirtyBitType::EXTENDED_DIRTY_BIT_CLIP_CONTROL:
updateViewport(vk::GetImpl(glState.getDrawFramebuffer()),
glState.getViewport(), glState.getNearPlane(),
glState.getFarPlane(),
isViewportFlipEnabledForDrawFBO());
glState.getFarPlane());
// Since we are flipping the y coordinate, update front face state
mGraphicsPipelineDesc->updateFrontFace(&mGraphicsPipelineTransition,
glState.getRasterizerState(),
@@ -3258,7 +3259,8 @@ void ContextVk::updateGraphicsPipelineDescWithSpecConstUsageBits(SpecConstUsageB
SurfaceRotation rotationAndFlip = mCurrentRotationDrawFramebuffer;
ASSERT(ToUnderlying(rotationAndFlip) < ToUnderlying(SurfaceRotation::FlippedIdentity));
bool yFlipped =
isViewportFlipEnabledForDrawFBO() && usageBits.test(sh::vk::SpecConstUsage::YFlip);
isViewportFlipEnabledForDrawFBO() && (usageBits.test(sh::vk::SpecConstUsage::YFlip) ||
!getFeatures().supportsNegativeViewport.enabled);
// usageBits are only set when specialization constants are used. With gl_Position pre-rotation
// handled by the SPIR-V transformer, we need to have this information even when the driver

View File

@@ -737,8 +737,7 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
void updateViewport(FramebufferVk *framebufferVk,
const gl::Rectangle &viewport,
float nearPlane,
float farPlane,
bool invertViewport);
float farPlane);
void updateDepthRange(float nearPlane, float farPlane);
void updateFlipViewportDrawFramebuffer(const gl::State &glState);
void updateFlipViewportReadFramebuffer(const gl::State &glState);

View File

@@ -33,8 +33,10 @@ bool ValidateTransformedSpirV(ContextVk *contextVk,
for (gl::ShaderType shaderType : linkedShaderStages)
{
GlslangSpirvOptions options;
options.shaderType = shaderType;
options.preRotation = SurfaceRotation::FlippedRotated90Degrees;
options.shaderType = shaderType;
options.preRotation = SurfaceRotation::FlippedRotated90Degrees;
options.negativeViewportSupported =
contextVk->getFeatures().supportsNegativeViewport.enabled;
options.transformPositionToVulkanClipSpace = true;
options.removeDebugInfo = true;
options.isTransformFeedbackStage = shaderType == lastPreFragmentStage;
@@ -135,8 +137,9 @@ angle::Result ProgramInfo::initProgram(ContextVk *contextVk,
options.shaderType = shaderType;
options.removeEarlyFragmentTestsOptimization =
shaderType == gl::ShaderType::Fragment && optionBits.removeEarlyFragmentTestsOptimization;
options.removeDebugInfo = !contextVk->getRenderer()->getEnableValidationLayers();
options.isTransformFeedbackStage = isLastPreFragmentStage;
options.removeDebugInfo = !contextVk->getRenderer()->getEnableValidationLayers();
options.isTransformFeedbackStage = isLastPreFragmentStage;
options.negativeViewportSupported = contextVk->getFeatures().supportsNegativeViewport.enabled;
if (isLastPreFragmentStage)
{

View File

@@ -2010,6 +2010,10 @@ void RendererVk::initFeatures(DisplayVk *displayVk,
bool isSwiftShader =
IsSwiftshader(mPhysicalDeviceProperties.vendorID, mPhysicalDeviceProperties.deviceID);
bool supportsNegativeViewport =
ExtensionFound(VK_KHR_MAINTENANCE1_EXTENSION_NAME, deviceExtensionNames) ||
mPhysicalDeviceProperties.apiVersion >= VK_API_VERSION_1_1;
if (mLineRasterizationFeatures.bresenhamLines == VK_TRUE)
{
ASSERT(mLineRasterizationFeatures.sType ==
@@ -2196,7 +2200,8 @@ void RendererVk::initFeatures(DisplayVk *displayVk,
ExtensionFound(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME, deviceExtensionNames));
// Android pre-rotation support can be disabled.
ANGLE_FEATURE_CONDITION(&mFeatures, enablePreRotateSurfaces, IsAndroid());
ANGLE_FEATURE_CONDITION(&mFeatures, enablePreRotateSurfaces,
IsAndroid() && supportsNegativeViewport);
// Currently disabled by default: http://anglebug.com/3078
ANGLE_FEATURE_CONDITION(
@@ -2278,6 +2283,9 @@ void RendererVk::initFeatures(DisplayVk *displayVk,
// required.
ANGLE_FEATURE_CONDITION(&mFeatures, emulateR32fImageAtomicExchange, true);
// Negative viewports are exposed in the Maintenance1 extension and in core Vulkan 1.1+.
ANGLE_FEATURE_CONDITION(&mFeatures, supportsNegativeViewport, supportsNegativeViewport);
angle::PlatformMethods *platform = ANGLEPlatformCurrent();
platform->overrideFeaturesVk(platform, &mFeatures);

View File

@@ -1212,13 +1212,15 @@ ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND(
WithMetalForcedBufferGPUStorage(ES3_METAL()),
WithMetalMemoryBarrierAndCheapRenderPass(ES3_METAL(),
/* hasBarrier */ false,
/* cheapRenderPass */ false));
/* cheapRenderPass */ false),
WithNoVulkanViewportFlip(ES2_VULKAN()));
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND(
TriangleFanDrawTest,
WithMetalForcedBufferGPUStorage(ES3_METAL()),
WithMetalMemoryBarrierAndCheapRenderPass(ES3_METAL(),
/* hasBarrier */ false,
/* cheapRenderPass */ false));
/* cheapRenderPass */ false),
WithNoVulkanViewportFlip(ES2_VULKAN()));
} // namespace

View File

@@ -254,6 +254,15 @@ std::ostream &operator<<(std::ostream &stream, const PlatformParameters &pp)
stream << "_ForceMetalBufferGPUStorage";
}
if (pp.eglParameters.supportsVulkanViewportFlip == EGL_TRUE)
{
stream << "_VulkanViewportFlip";
}
else if (pp.eglParameters.supportsVulkanViewportFlip == EGL_FALSE)
{
stream << "_NoVulkanViewportFlip";
}
return stream;
}

View File

@@ -281,6 +281,13 @@ inline PlatformParameters WithAsyncCommandQueueFeatureVulkan(const PlatformParam
withAsyncCommandQueue.eglParameters.asyncCommandQueueFeatureVulkan = EGL_TRUE;
return withAsyncCommandQueue;
}
inline PlatformParameters WithNoVulkanViewportFlip(const PlatformParameters &params)
{
PlatformParameters withoutVulkanViewportFlip = params;
withoutVulkanViewportFlip.eglParameters.supportsVulkanViewportFlip = EGL_FALSE;
return withoutVulkanViewportFlip;
}
} // namespace angle
#endif // ANGLE_TEST_CONFIGS_H_

View File

@@ -564,6 +564,7 @@ bool IsConfigAllowlisted(const SystemInfo &systemInfo, const PlatformParameters
switch (param.getRenderer())
{
case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:
return true;
case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
// Swiftshader's vulkan frontend doesn't build on Android.
if (param.getDeviceType() == EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE)
@@ -574,6 +575,10 @@ bool IsConfigAllowlisted(const SystemInfo &systemInfo, const PlatformParameters
{
return false;
}
if (param.eglParameters.supportsVulkanViewportFlip == EGL_FALSE)
{
return false;
}
return true;
default:
return false;

View File

@@ -64,7 +64,7 @@ struct EGLPlatformParameters
shaderStencilOutputFeature, genMultipleMipsPerPassFeature, platformMethods,
robustness, emulatedPrerotation, asyncCommandQueueFeatureVulkan,
hasExplicitMemBarrierFeatureMtl, hasCheapRenderPassFeatureMtl,
forceBufferGPUStorageFeatureMtl);
forceBufferGPUStorageFeatureMtl, supportsVulkanViewportFlip);
}
EGLint renderer = EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE;
@@ -85,6 +85,7 @@ struct EGLPlatformParameters
EGLint hasExplicitMemBarrierFeatureMtl = EGL_DONT_CARE;
EGLint hasCheapRenderPassFeatureMtl = EGL_DONT_CARE;
EGLint forceBufferGPUStorageFeatureMtl = EGL_DONT_CARE;
EGLint supportsVulkanViewportFlip = EGL_DONT_CARE;
angle::PlatformMethods *platformMethods = nullptr;
};

View File

@@ -206,6 +206,15 @@ bool EGLWindow::initializeDisplay(OSWindow *osWindow,
disabledFeatureOverrides.push_back("gen_multiple_mips_per_pass");
}
if (params.supportsVulkanViewportFlip == EGL_TRUE)
{
enabledFeatureOverrides.push_back("supportsViewportFlip");
}
else if (params.supportsVulkanViewportFlip == EGL_FALSE)
{
disabledFeatureOverrides.push_back("supportsViewportFlip");
}
switch (params.emulatedPrerotation)
{
case 90: