Commit 80a4223e by Mohan Maiya Committed by Commit Bot

Vulkan: Handle changes to viewport when clip origin is modified

The expected view port is different from current viewport translation when the clip origin is the upper left. So now, it has four different view port translations based on clip origin and y-flip of framebuffer. - add query and state management for EXT_clip_control - add dirty bit for clip control - change viewport, scissor and cull face when clip origin changes Bug: angleproject:5471 Tests: dEQP-GLES2.functional.clip_control.* Change-Id: I78dc752c3287b09f25496034e0d0d2724138010c Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2615863 Commit-Queue: Mohan Maiya <m.maiya@samsung.com> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 6cfbe252
......@@ -1035,6 +1035,7 @@ const ExtensionInfoMap &GetExtensionInfoMap()
map["GL_EXT_draw_elements_base_vertex"] = enableableExtension(&Extensions::drawElementsBaseVertexEXT);
map["GL_EXT_gpu_shader5"] = enableableExtension(&Extensions::gpuShader5EXT);
map["GL_APPLE_clip_distance"] = enableableExtension(&Extensions::clipDistanceAPPLE);
map["GL_EXT_clip_control"] = enableableExtension(&Extensions::clipControlEXT);
map["GL_EXT_EGL_image_array"] = enableableExtension(&Extensions::eglImageArray);
map["GL_EXT_buffer_storage"] = enableableExtension(&Extensions::bufferStorageEXT);
map["GL_EXT_external_buffer"] = enableableExtension(&Extensions::externalBufferEXT);
......
......@@ -655,6 +655,9 @@ struct Extensions
// GL_APPLE_clip_distance
bool clipDistanceAPPLE = false;
// GL_EXT_clip_control
bool clipControlEXT = false;
// GL_OES_texture_cube_map_array
bool textureCubeMapArrayOES = false;
// GL_EXT_texture_cube_map_array
......
......@@ -2020,6 +2020,14 @@ void Context::getIntegervImpl(GLenum pname, GLint *params) const
*params = mState.mCaps.textureBufferOffsetAlignment;
break;
// GL_EXT_clip_control
case GL_CLIP_ORIGIN_EXT:
*params = mState.mClipControlOrigin;
break;
case GL_CLIP_DEPTH_MODE_EXT:
*params = mState.mClipControlDepth;
break;
default:
ANGLE_CONTEXT_TRY(mState.getIntegerv(this, pname, params));
break;
......@@ -5287,6 +5295,11 @@ void Context::depthRangef(GLfloat zNear, GLfloat zFar)
mState.setDepthRange(clamp01(zNear), clamp01(zFar));
}
void Context::clipControl(GLenum origin, GLenum depth)
{
mState.setClipControl(origin, depth);
}
void Context::disable(GLenum cap)
{
mState.setEnableFeature(cap, false);
......
......@@ -2935,11 +2935,6 @@ void Context::clearNamedFramebufferuiv(FramebufferID framebuffer,
UNIMPLEMENTED();
}
void Context::clipControl(GLenum origin, GLenum depth)
{
UNIMPLEMENTED();
}
void Context::compressedTextureSubImage1D(TextureID texture,
GLint level,
GLint xoffset,
......
......@@ -416,6 +416,9 @@ void State::initialize(Context *context)
mNearZ = 0.0f;
mFarZ = 1.0f;
mClipControlOrigin = GL_LOWER_LEFT_EXT;
mClipControlDepth = GL_NEGATIVE_ONE_TO_ONE_EXT;
mActiveSampler = 0;
mVertexAttribCurrentValues.resize(caps.maxVertexAttributes);
......@@ -817,6 +820,21 @@ void State::setDepthRange(float zNear, float zFar)
}
}
void State::setClipControl(GLenum origin, GLenum depth)
{
if (mClipControlOrigin != origin)
{
mClipControlOrigin = origin;
mDirtyBits.set(DIRTY_BIT_EXTENDED);
}
if (mClipControlDepth != depth)
{
mClipControlDepth = depth;
mDirtyBits.set(DIRTY_BIT_EXTENDED);
}
}
void State::setBlend(bool enabled)
{
mBlendState.blend = enabled;
......@@ -2366,6 +2384,16 @@ void State::getBooleanv(GLenum pname, GLboolean *params) const
case GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED:
*params = isPrimitiveRestartEnabled() && getExtensions().tessellationShaderEXT;
break;
// 2.2.2 Data Conversions For State Query Commands, in GLES 3.2 spec.
// If a command returning boolean data is called, such as GetBooleanv, a floating-point or
// integer value converts to FALSE if and only if it is zero. Otherwise it converts to TRUE.
// GL_EXT_clip_control
case GL_CLIP_ORIGIN_EXT:
*params = GL_TRUE;
break;
case GL_CLIP_DEPTH_MODE_EXT:
*params = GL_TRUE;
break;
default:
UNREACHABLE();
break;
......@@ -2482,6 +2510,15 @@ void State::getFloatv(GLenum pname, GLfloat *params) const
case GL_MIN_SAMPLE_SHADING_VALUE:
*params = mMinSampleShading;
break;
// 2.2.2 Data Conversions For State Query Commands, in GLES 3.2 spec.
// If a command returning floating-point data is called, such as GetFloatv, ... An integer
// value is coerced to floating-point.
case GL_CLIP_ORIGIN_EXT:
*params = static_cast<float>(mClipControlOrigin);
break;
case GL_CLIP_DEPTH_MODE_EXT:
*params = static_cast<float>(mClipControlDepth);
break;
default:
UNREACHABLE();
break;
......@@ -2948,6 +2985,13 @@ angle::Result State::getIntegerv(const Context *context, GLenum pname, GLint *pa
*params = mPatchVertices;
break;
// GL_EXT_clip_control
case GL_CLIP_ORIGIN_EXT:
*params = mClipControlOrigin;
break;
case GL_CLIP_DEPTH_MODE_EXT:
*params = mClipControlDepth;
break;
default:
UNREACHABLE();
break;
......
......@@ -173,6 +173,15 @@ class State : angle::NonCopyable
float getNearPlane() const { return mNearZ; }
float getFarPlane() const { return mFarZ; }
// Clip control extension
void setClipControl(GLenum origin, GLenum depth);
bool isClipControlDepthZeroToOne() const { return mClipControlDepth == GL_ZERO_TO_ONE_EXT; }
gl::ClipSpaceOrigin getClipSpaceOrigin() const
{
return mClipControlOrigin == GL_UPPER_LEFT_EXT ? ClipSpaceOrigin::UpperLeft
: ClipSpaceOrigin::LowerLeft;
}
// Blend state manipulation
bool isBlendEnabled() const { return mBlendStateExt.mEnabledMask.test(0); }
bool isBlendEnabledIndexed(GLuint index) const
......@@ -651,7 +660,8 @@ class State : angle::NonCopyable
DIRTY_BIT_PROVOKING_VERTEX,
DIRTY_BIT_SAMPLE_SHADING,
DIRTY_BIT_PATCH_VERTICES,
DIRTY_BIT_EXTENDED, // clip distances, mipmap generation hint, derivative hint.
DIRTY_BIT_EXTENDED, // clip distances, mipmap generation hint, derivative hint,
// EXT_clip_control
DIRTY_BIT_INVALID,
DIRTY_BIT_MAX = DIRTY_BIT_INVALID,
};
......@@ -989,6 +999,9 @@ class State : angle::NonCopyable
float mNearZ;
float mFarZ;
GLenum mClipControlOrigin;
GLenum mClipControlDepth;
Framebuffer *mReadFramebuffer;
Framebuffer *mDrawFramebuffer;
BindingPointer<Renderbuffer> mRenderbuffer;
......
......@@ -83,6 +83,12 @@ struct Rectangle
bool operator==(const Rectangle &a, const Rectangle &b);
bool operator!=(const Rectangle &a, const Rectangle &b);
enum class ClipSpaceOrigin
{
LowerLeft = 0,
UpperLeft = 1
};
// Calculate the intersection of two rectangles. Returns false if the intersection is empty.
bool ClipRectangle(const Rectangle &source, const Rectangle &clip, Rectangle *intersection);
// Calculate the smallest rectangle that covers both rectangles. This rectangle may cover areas
......
......@@ -3270,6 +3270,7 @@ bool GetQueryParameterInfo(const State &glState,
*numParams = 1;
return true;
case GL_MAX_CULL_DISTANCES_EXT:
case GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES_EXT:
if (!extensions.clipCullDistanceEXT)
{
return false;
......@@ -3277,8 +3278,9 @@ bool GetQueryParameterInfo(const State &glState,
*type = GL_INT;
*numParams = 1;
return true;
case GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES_EXT:
if (!extensions.clipCullDistanceEXT)
case GL_CLIP_ORIGIN_EXT:
case GL_CLIP_DEPTH_MODE_EXT:
if (!extensions.clipControlEXT)
{
return false;
}
......
......@@ -2071,6 +2071,7 @@ angle::Result StateManagerGL::syncState(const gl::Context *context,
setClipDistancesEnable(state.getEnabledClipDistances());
// TODO(jmadill): handle mipmap generation hint
// TODO(jmadill): handle shader derivative hint
// Nothing to do until EXT_clip_contorl is implemented.
break;
case gl::State::DIRTY_BIT_SAMPLE_SHADING:
// Nothing to do until OES_sample_shading is implemented.
......
......@@ -1044,6 +1044,7 @@ angle::Result ContextMtl::syncState(const gl::Context *context,
break;
case gl::State::DIRTY_BIT_EXTENDED:
updateExtendedState(glState);
// Nothing to do until EXT_clip_control is implemented.
break;
case gl::State::DIRTY_BIT_SAMPLE_SHADING:
// Nothing to do until OES_sample_shading is implemented.
......
......@@ -368,6 +368,7 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk
mXfbVertexCountPerInstance(0),
mClearColorMasks(0),
mFlipYForCurrentSurface(false),
mClipSpaceOrigin(gl::ClipSpaceOrigin::LowerLeft),
mIsAnyHostVisibleBufferWritten(false),
mEmulateSeamfulCubeMapSampling(false),
mOutsideRenderPassCommands(nullptr),
......@@ -2642,11 +2643,15 @@ void ContextVk::updateViewport(FramebufferVk *framebufferVk,
correctedRect, &rotatedRect);
VkViewport vkViewport;
gl_vk::GetViewport(rotatedRect, nearPlane, farPlane, invertViewport,
// If the surface is rotated 90/270 degrees, use the framebuffer's width
// instead of the height for calculating the final viewport.
isRotatedAspectRatioForDrawFBO() ? fbDimensions.width : fbDimensions.height,
&vkViewport);
gl_vk::GetViewport(
rotatedRect, nearPlane, farPlane, invertViewport,
// If clip space origin is upper left, viewport origin's y value will be offset by the
// height of the viewport when clip space is mapped into screen space.
mClipSpaceOrigin == gl::ClipSpaceOrigin::UpperLeft,
// If the surface is rotated 90/270 degrees, use the framebuffer's width instead of the
// height for calculating the final viewport.
isRotatedAspectRatioForDrawFBO() ? fbDimensions.width : fbDimensions.height, &vkViewport);
mGraphicsPipelineDesc->updateViewport(&mGraphicsPipelineTransition, vkViewport);
invalidateGraphicsDriverUniforms();
}
......@@ -2904,7 +2909,7 @@ angle::Result ContextVk::syncState(const gl::Context *context,
case gl::State::DIRTY_BIT_FRONT_FACE:
mGraphicsPipelineDesc->updateFrontFace(&mGraphicsPipelineTransition,
glState.getRasterizerState(),
isViewportFlipEnabledForDrawFBO());
isYFlipEnabledForDrawFBO());
break;
case gl::State::DIRTY_BIT_POLYGON_OFFSET_FILL_ENABLED:
mGraphicsPipelineDesc->updatePolygonOffsetFillEnabled(
......@@ -2980,9 +2985,10 @@ angle::Result ContextVk::syncState(const gl::Context *context,
glState.getFarPlane(), isViewportFlipEnabledForDrawFBO());
updateColorMasks(glState.getBlendStateExt());
updateRasterizationSamples(mDrawFramebuffer->getSamples());
mGraphicsPipelineDesc->updateFrontFace(&mGraphicsPipelineTransition,
glState.getRasterizerState(),
isViewportFlipEnabledForDrawFBO());
isYFlipEnabledForDrawFBO());
updateScissor(glState);
const gl::DepthStencilState depthStencilState = glState.getDepthStencilState();
mGraphicsPipelineDesc->updateDepthTestEnabled(&mGraphicsPipelineTransition,
......@@ -3079,10 +3085,30 @@ angle::Result ContextVk::syncState(const gl::Context *context,
case gl::State::DIRTY_BIT_PROVOKING_VERTEX:
break;
case gl::State::DIRTY_BIT_EXTENDED:
{
// Handling clip distance enabled flags, mipmap generation hint & shader derivative
// hint.
invalidateGraphicsDriverUniforms();
// Handling clip space origin for EXT_clip_control.
if (glState.getClipSpaceOrigin() != getClipSpaceOrigin())
{
updateClipSpaceOrigin(glState);
updateViewport(vk::GetImpl(glState.getDrawFramebuffer()), glState.getViewport(),
glState.getNearPlane(), glState.getFarPlane(),
isViewportFlipEnabledForDrawFBO());
// Since we are flipping the y coordinate, update front face state
mGraphicsPipelineDesc->updateFrontFace(&mGraphicsPipelineTransition,
glState.getRasterizerState(),
isYFlipEnabledForDrawFBO());
updateScissor(glState);
// Nothing is needed for depth correction for EXT_clip_control.
// glState will be used to toggle control path of depth correction code in
// SPIR-V tranform options.
}
break;
}
case gl::State::DIRTY_BIT_PATCH_VERTICES:
mGraphicsPipelineDesc->updatePatchVertices(&mGraphicsPipelineTransition,
glState.getPatchVertices());
......@@ -3281,6 +3307,16 @@ void ContextVk::updateSurfaceRotationReadFramebuffer(const gl::State &glState)
DetermineSurfaceRotation(readFramebuffer, mCurrentWindowSurface);
}
gl::ClipSpaceOrigin ContextVk::getClipSpaceOrigin() const
{
return mClipSpaceOrigin;
}
void ContextVk::updateClipSpaceOrigin(const gl::State &glState)
{
mClipSpaceOrigin = glState.getClipSpaceOrigin();
}
gl::Caps ContextVk::getNativeCaps() const
{
return mRenderer->getNativeCaps();
......
......@@ -208,6 +208,20 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
SurfaceRotation getRotationDrawFramebuffer() const;
SurfaceRotation getRotationReadFramebuffer() const;
// View port (x, y, w, h) will be determined by a combination of -
// 1. clip space origin
// 2. isViewportFlipEnabledForDrawFBO
// For userdefined FBOs it will be based on the value of isViewportFlipEnabledForDrawFBO.
// For default FBOs it will be XOR of ClipOrigin and isViewportFlipEnabledForDrawFBO.
// isYFlipEnabledForDrawFBO indicates the rendered image is upside-down.
ANGLE_INLINE bool isYFlipEnabledForDrawFBO() const
{
return mClipSpaceOrigin == gl::ClipSpaceOrigin::UpperLeft
? !isViewportFlipEnabledForDrawFBO()
: isViewportFlipEnabledForDrawFBO();
}
gl::ClipSpaceOrigin getClipSpaceOrigin() const;
void invalidateProgramBindingHelper(const gl::State &glState);
angle::Result invalidateProgramExecutableHelper(const gl::Context *context);
......@@ -735,6 +749,8 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
void updateSurfaceRotationDrawFramebuffer(const gl::State &glState);
void updateSurfaceRotationReadFramebuffer(const gl::State &glState);
void updateClipSpaceOrigin(const gl::State &glState);
angle::Result updateActiveTextures(const gl::Context *context);
angle::Result updateActiveImages(const gl::Context *context,
vk::CommandBufferHelper *commandBufferHelper);
......@@ -941,6 +957,9 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
bool mFlipViewportForDrawFramebuffer;
bool mFlipViewportForReadFramebuffer;
// Cache clip origin state, needed for viewport calculation.
gl::ClipSpaceOrigin mClipSpaceOrigin;
// If any host-visible buffer is written by the GPU since last submission, a barrier is inserted
// at the end of the command buffer to make that write available to the host.
bool mIsAnyHostVisibleBufferWritten;
......
......@@ -1571,11 +1571,13 @@ angle::Result UtilsVk::clearFramebuffer(ContextVk *contextVk,
VkViewport viewport;
gl::Rectangle completeRenderArea = framebuffer->getRotatedCompleteRenderArea(contextVk);
bool invertViewport = contextVk->isViewportFlipEnabledForDrawFBO();
bool clipSpaceOriginUpperLeft =
contextVk->getClipSpaceOrigin() == gl::ClipSpaceOrigin::UpperLeft;
// Set depth range to clear value. If clearing depth, the vertex shader depth output is clamped
// to this value, thus clearing the depth buffer to the desired clear value.
const float clearDepthValue = params.depthStencilClearValue.depth;
gl_vk::GetViewport(completeRenderArea, clearDepthValue, clearDepthValue, invertViewport,
completeRenderArea.height, &viewport);
clipSpaceOriginUpperLeft, completeRenderArea.height, &viewport);
pipelineDesc.setViewport(viewport);
// Scissored clears can create a large number of pipelines in some tests. Use dynamic state for
......@@ -1801,7 +1803,8 @@ angle::Result UtilsVk::blitResolveImpl(ContextVk *contextVk,
VkViewport viewport;
gl::Rectangle completeRenderArea = framebuffer->getRotatedCompleteRenderArea(contextVk);
gl_vk::GetViewport(completeRenderArea, 0.0f, 1.0f, false, completeRenderArea.height, &viewport);
gl_vk::GetViewport(completeRenderArea, 0.0f, 1.0f, false, false, completeRenderArea.height,
&viewport);
pipelineDesc.setViewport(viewport);
pipelineDesc.setScissor(gl_vk::GetRect(params.blitArea));
......@@ -2235,7 +2238,7 @@ angle::Result UtilsVk::copyImage(ContextVk *contextVk,
}
VkViewport viewport;
gl_vk::GetViewport(renderArea, 0.0f, 1.0f, false, dest->getExtents().height, &viewport);
gl_vk::GetViewport(renderArea, 0.0f, 1.0f, false, false, dest->getExtents().height, &viewport);
pipelineDesc.setViewport(viewport);
VkRect2D scissor = gl_vk::GetRect(renderArea);
......@@ -2690,8 +2693,10 @@ angle::Result UtilsVk::unresolve(ContextVk *contextVk,
VkViewport viewport;
gl::Rectangle completeRenderArea = framebuffer->getRotatedCompleteRenderArea(contextVk);
bool invertViewport = contextVk->isViewportFlipEnabledForDrawFBO();
gl_vk::GetViewport(completeRenderArea, 0.0f, 1.0f, invertViewport, completeRenderArea.height,
&viewport);
bool clipSpaceOriginUpperLeft =
contextVk->getClipSpaceOrigin() == gl::ClipSpaceOrigin::UpperLeft;
gl_vk::GetViewport(completeRenderArea, 0.0f, 1.0f, invertViewport, clipSpaceOriginUpperLeft,
completeRenderArea.height, &viewport);
pipelineDesc.setViewport(viewport);
pipelineDesc.setScissor(gl_vk::GetRect(completeRenderArea));
......
......@@ -1310,6 +1310,7 @@ void GetViewport(const gl::Rectangle &viewport,
float nearPlane,
float farPlane,
bool invertViewport,
bool clipSpaceOriginUpperLeft,
GLint renderAreaHeight,
VkViewport *viewportOut)
{
......@@ -1320,11 +1321,61 @@ void GetViewport(const gl::Rectangle &viewport,
viewportOut->minDepth = gl::clamp01(nearPlane);
viewportOut->maxDepth = gl::clamp01(farPlane);
// Say an application intends to draw a primitive (shown as 'o' below), it can choose to use
// different clip space origin. When clip space origin (shown as 'C' below) is switched from
// lower-left to upper-left, primitives will be rendered with its y-coordinate flipped.
// Rendered content will differ based on whether it is a default framebuffer or a user defined
// framebuffer. We modify the viewport's 'y' and 'h' accordingly.
// clip space origin is lower-left
// Expected draw in GLES default framebuffer user defined framebuffer
// (0,H) (0,0) (0,0)
// + +-----------+ (W,0) +-----------+ (W,0)
// | | | C----+
// | | | | | (h)
// | +----+ | +----+ | | O |
// | | O | | | O | (-h) | +----+
// | | | | | | |
// | C----+ | C----+ |
// +-----------+ (W,0) + +
// (0,0) (0,H) (0,H)
// y' = H - h y' = y
// clip space origin is upper-left
// Expected draw in GLES default framebuffer user defined framebuffer
// (0,H) (0,0) (0,0)
// + +-----------+ (W,0) +-----------+ (W,0)
// | | | +----+
// | | | | O | (-h)
// | C----+ | C----+ | | |
// | | | | | | (h) | C----+
// | | O | | | O | |
// | +----+ | +----+ |
// +-----------+ (W,0) + +
// (0,0) (0,H) (0,H)
// y' = H - (y + h) y' = y + H
if (clipSpaceOriginUpperLeft)
{
if (invertViewport)
{
viewportOut->y = static_cast<float>(renderAreaHeight - (viewport.height + viewport.y));
}
else
{
viewportOut->y = static_cast<float>(viewport.height + viewport.y);
viewportOut->height = -viewportOut->height;
}
}
else
{
if (invertViewport)
{
viewportOut->y = static_cast<float>(renderAreaHeight - viewport.y);
viewportOut->height = -viewportOut->height;
}
}
}
void GetExtentsAndLayerCount(gl::TextureType textureType,
......
......@@ -960,6 +960,7 @@ void GetViewport(const gl::Rectangle &viewport,
float nearPlane,
float farPlane,
bool invertViewport,
bool upperLeftOrigin,
GLint renderAreaHeight,
VkViewport *viewportOut);
......
......@@ -1338,7 +1338,19 @@ bool ValidateBufferStorageEXT(const Context *context,
// GL_EXT_clip_control
bool ValidateClipControlEXT(const Context *context, GLenum origin, GLenum depth)
{
if ((origin != GL_LOWER_LEFT_EXT) && (origin != GL_UPPER_LEFT_EXT))
{
context->validationError(GL_INVALID_ENUM, kInvalidOriginEnum);
return false;
}
if ((depth != GL_NEGATIVE_ONE_TO_ONE_EXT) && (depth != GL_ZERO_TO_ONE_EXT))
{
context->validationError(GL_INVALID_ENUM, kInvalidDepthEnum);
return false;
}
return true;
}
// GL_EXT_external_buffer
......
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