Commit b6adeb2f by Shahbaz Youssefi Committed by Angle LUCI CQ

Vulkan: Use pipeline statistics query to emulate primitives generated

The VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT query produces the same result as the GL primitives generated query. One caveat is that in combination with rasterizer discard this query may not work. This is emulated by disabling rasterizer discard when this query is active and applying an empty scissor instead. When VK_EXT_primitives_generated_query is released and supported, a similar issue with rasterizer discard persists so this change will facilitate using that extension as well. Bug: angleproject:5430 Change-Id: Id45b6f058c5cb6837e04aa64b1efde28c104e4cf Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2976181 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com>
parent 09bd5578
......@@ -161,11 +161,17 @@ struct FeaturesVk : FeatureSetBase
"VkDevice supports the EGL_ANDROID_native_fence_sync extension", &members,
"http://anglebug.com/2517"};
// Whether the VkDevice can support imageCubeArray feature properly.
// Whether the VkDevice can support the imageCubeArray feature properly.
Feature supportsImageCubeArray = {"supportsImageCubeArray", FeatureCategory::VulkanFeatures,
"VkDevice supports the imageCubeArray feature properly",
&members, "http://anglebug.com/3584"};
// Whether the VkDevice supports the pipelineStatisticsQuery feature.
Feature supportsPipelineStatisticsQuery = {
"supportsPipelineStatisticsQuery", FeatureCategory::VulkanFeatures,
"VkDevice supports the pipelineStatisticsQuery feature", &members,
"http://anglebug.com/5430"};
// Whether the VkDevice supports the VK_EXT_shader_stencil_export extension, which is used to
// perform multisampled resolve of stencil buffer. A multi-step workaround is used instead if
// this extension is not available.
......
......@@ -678,7 +678,16 @@ angle::Result ContextVk::initialize()
vk::kDefaultTransformFeedbackQueryPoolSize));
}
// Init gles to vulkan index type map
// The primitives generated query is provided through the Vulkan pipeline statistics query if
// supported. TODO: If VK_EXT_primitives_generated_query is supported, use that instead.
// http://anglebug.com/5430
if (getFeatures().supportsPipelineStatisticsQuery.enabled)
{
ANGLE_TRY(mQueryPools[gl::QueryType::PrimitivesGenerated].init(
this, VK_QUERY_TYPE_PIPELINE_STATISTICS, vk::kDefaultPrimitivesGeneratedQueryPoolSize));
}
// Init GLES to Vulkan index type map.
initIndexTypeMap();
// Init driver uniforms and get the descriptor set layouts.
......@@ -1997,13 +2006,31 @@ angle::Result ContextVk::handleDirtyGraphicsViewport(DirtyBits::Iterator *dirtyB
mRenderPassCommandBuffer->setViewport(0, 1, &mViewport);
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsScissor(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
mRenderPassCommandBuffer->setScissor(0, 1, &mScissor);
handleDirtyGraphicsScissorImpl(mState.isQueryActive(gl::QueryType::PrimitivesGenerated));
return angle::Result::Continue;
}
void ContextVk::handleDirtyGraphicsScissorImpl(bool isPrimitivesGeneratedQueryActive)
{
// If primitives generated query and rasterizer discard are both active, but the Vulkan
// implementation of the query does not support rasterizer discard, use an empty scissor to
// emulate it.
if (isEmulatingRasterizerDiscardDuringPrimitivesGeneratedQuery(
isPrimitivesGeneratedQueryActive))
{
VkRect2D emptyScissor = {};
mRenderPassCommandBuffer->setScissor(0, 1, &emptyScissor);
}
else
{
mRenderPassCommandBuffer->setScissor(0, 1, &mScissor);
}
}
angle::Result ContextVk::handleDirtyComputeDescriptorSets()
{
return handleDirtyDescriptorSetsImpl(&mOutsideRenderPassCommands->getCommandBuffer());
......@@ -3324,6 +3351,39 @@ void ContextVk::updateRasterizationSamples(const uint32_t rasterizationSamples)
updateSampleMaskWithRasterizationSamples(rasterizationSamples);
}
void ContextVk::updateRasterizerDiscardEnabled(bool isPrimitivesGeneratedQueryActive)
{
// On some devices, when rasterizerDiscardEnable is enabled, the
// VK_EXT_primitives_generated_query as well as the pipeline statistics query used to emulate it
// are non-functional. For VK_EXT_primitives_generated_query there's a feature bit but not for
// pipeline statistics query. If the primitives generated query is active (and rasterizer
// discard is not supported), rasterizerDiscardEnable is set to false and the functionality
// is otherwise emulated (by using an empty scissor).
// If the primitives generated query implementation supports rasterizer discard, just set
// rasterizer discard as requested. Otherwise disable it.
bool isRasterizerDiscardEnabled = mState.isRasterizerDiscardEnabled();
bool isEmulatingRasterizerDiscard = isEmulatingRasterizerDiscardDuringPrimitivesGeneratedQuery(
isPrimitivesGeneratedQueryActive);
mGraphicsPipelineDesc->updateRasterizerDiscardEnabled(
&mGraphicsPipelineTransition, isRasterizerDiscardEnabled && !isEmulatingRasterizerDiscard);
invalidateCurrentGraphicsPipeline();
if (!isEmulatingRasterizerDiscard)
{
return;
}
// If we are emulating rasterizer discard, update the scissor if in render pass. If not in
// render pass, DIRTY_BIT_SCISSOR will be set when the render pass next starts.
if (hasStartedRenderPass())
{
handleDirtyGraphicsScissorImpl(isPrimitivesGeneratedQueryActive);
}
}
void ContextVk::invalidateProgramBindingHelper(const gl::State &glState)
{
mProgram = nullptr;
......@@ -3543,8 +3603,8 @@ angle::Result ContextVk::syncState(const gl::Context *context,
glState.getRasterizerState());
break;
case gl::State::DIRTY_BIT_RASTERIZER_DISCARD_ENABLED:
mGraphicsPipelineDesc->updateRasterizerDiscardEnabled(
&mGraphicsPipelineTransition, glState.isRasterizerDiscardEnabled());
updateRasterizerDiscardEnabled(
mState.isQueryActive(gl::QueryType::PrimitivesGenerated));
break;
case gl::State::DIRTY_BIT_LINE_WIDTH:
mGraphicsPipelineDesc->updateLineWidth(&mGraphicsPipelineTransition,
......@@ -3613,6 +3673,8 @@ angle::Result ContextVk::syncState(const gl::Context *context,
glState.getFarPlane());
updateColorMasks(glState.getBlendStateExt());
updateRasterizationSamples(mDrawFramebuffer->getSamples());
updateRasterizerDiscardEnabled(
mState.isQueryActive(gl::QueryType::PrimitivesGenerated));
mGraphicsPipelineDesc->updateFrontFace(&mGraphicsPipelineTransition,
glState.getRasterizerState(),
......@@ -4544,9 +4606,17 @@ vk::DynamicQueryPool *ContextVk::getQueryPool(gl::QueryType queryType)
queryType == gl::QueryType::TransformFeedbackPrimitivesWritten ||
queryType == gl::QueryType::Timestamp || queryType == gl::QueryType::TimeElapsed);
// For PrimitivesGenerated queries, use the same pool as TransformFeedbackPrimitivesWritten.
// They are served with the same Vulkan query.
if (queryType == gl::QueryType::PrimitivesGenerated)
// For PrimitivesGenerated queries:
//
// - If VK_EXT_primitives_generated_query is supported, use that.
// TODO: http://anglebug.com/5430
// - Otherwise, if pipelineStatisticsQuery is supported, use that,
// - Otherwise, use the same pool as TransformFeedbackPrimitivesWritten and share the query as
// the Vulkan transform feedback query produces both results. This option is non-conformant
// as the primitives generated query will not be functional without transform feedback.
//
if (queryType == gl::QueryType::PrimitivesGenerated &&
!getFeatures().supportsPipelineStatisticsQuery.enabled)
{
queryType = gl::QueryType::TransformFeedbackPrimitivesWritten;
}
......@@ -5748,6 +5818,12 @@ angle::Result ContextVk::beginRenderPassQuery(QueryVk *queryVk)
if (mRenderPassCommandBuffer)
{
ANGLE_TRY(queryVk->getQueryHelper()->beginRenderPassQuery(this));
// Update rasterizer discard emulation with primitives generated query if necessary.
if (queryVk->getType() == gl::QueryType::PrimitivesGenerated)
{
updateRasterizerDiscardEnabled(true);
}
}
gl::QueryType type = queryVk->getType();
......@@ -5766,6 +5842,12 @@ angle::Result ContextVk::endRenderPassQuery(QueryVk *queryVk)
if (mRenderPassCommandBuffer)
{
queryVk->getQueryHelper()->endRenderPassQuery(this);
// Update rasterizer discard emulation with primitives generated query if necessary.
if (queryVk->getType() == gl::QueryType::PrimitivesGenerated)
{
updateRasterizerDiscardEnabled(false);
}
}
gl::QueryType type = queryVk->getType();
......@@ -5788,6 +5870,9 @@ void ContextVk::pauseRenderPassQueriesIfActive()
if (activeQuery)
{
activeQuery->onRenderPassEnd(this);
// No need to update rasterizer discard emulation with primitives generated query. The
// state will be updated when the next render pass starts.
}
}
}
......@@ -5802,12 +5887,40 @@ angle::Result ContextVk::resumeRenderPassQueriesIfActive()
if (activeQuery)
{
ANGLE_TRY(activeQuery->onRenderPassStart(this));
// Update rasterizer discard emulation with primitives generated query if necessary.
if (activeQuery->getType() == gl::QueryType::PrimitivesGenerated)
{
updateRasterizerDiscardEnabled(true);
}
}
}
return angle::Result::Continue;
}
bool ContextVk::doesPrimitivesGeneratedQuerySupportRasterizerDiscard() const
{
// TODO: If primitives generated is implemented with VK_EXT_primitives_generated_query, check
// the corresponding feature bit. http://anglebug.com/5430.
// If primitives generated is emulated with pipeline statistics query, it's unknown on which
// hardware rasterizer discard is supported. Assume it's supported on none.
if (getFeatures().supportsPipelineStatisticsQuery.enabled)
{
return false;
}
return true;
}
bool ContextVk::isEmulatingRasterizerDiscardDuringPrimitivesGeneratedQuery(
bool isPrimitivesGeneratedQueryActive) const
{
return isPrimitivesGeneratedQueryActive && mState.isRasterizerDiscardEnabled() &&
!doesPrimitivesGeneratedQuerySupportRasterizerDiscard();
}
QueryVk *ContextVk::getActiveRenderPassQuery(gl::QueryType queryType) const
{
return mActiveRenderPassQueries[queryType];
......
......@@ -589,6 +589,9 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
angle::Result endRenderPassQuery(QueryVk *queryVk);
void pauseRenderPassQueriesIfActive();
angle::Result resumeRenderPassQueriesIfActive();
bool doesPrimitivesGeneratedQuerySupportRasterizerDiscard() const;
bool isEmulatingRasterizerDiscardDuringPrimitivesGeneratedQuery(
bool isPrimitivesGeneratedQueryActive) const;
// Used by QueryVk to share query helpers between transform feedback queries.
QueryVk *getActiveRenderPassQuery(gl::QueryType queryType) const;
......@@ -874,6 +877,7 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
VkPipelineBindPoint bindPoint,
DriverUniformsDescriptorSet *driverUniforms);
angle::Result handleDirtyDescriptorSetsImpl(vk::CommandBuffer *commandBuffer);
void handleDirtyGraphicsScissorImpl(bool isPrimitivesGeneratedQueryActive);
angle::Result allocateDriverUniforms(size_t driverUniformsSize,
DriverUniformsDescriptorSet *driverUniforms,
......@@ -951,6 +955,7 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
void updateSampleShadingWithRasterizationSamples(const uint32_t rasterizationSamples);
void updateRasterizationSamples(const uint32_t rasterizationSamples);
void updateRasterizerDiscardEnabled(bool isPrimitivesGeneratedQueryActive);
SpecConstUsageBits getCurrentProgramSpecConstUsageBits() const;
void updateGraphicsPipelineDescWithSpecConstUsageBits(SpecConstUsageBits usageBits);
......
......@@ -50,10 +50,21 @@ bool IsEmulatedTransformFeedbackQuery(ContextVk *contextVk, gl::QueryType type)
contextVk->getFeatures().emulateTransformFeedback.enabled;
}
bool IsPrimitivesGeneratedQueryShared(ContextVk *contextVk)
{
return !contextVk->getFeatures().supportsPipelineStatisticsQuery.enabled;
}
QueryVk *GetShareQuery(ContextVk *contextVk, gl::QueryType type)
{
QueryVk *shareQuery = nullptr;
// If the primitives generated query has its own dedicated Vulkan query, there's no sharing.
if (!IsPrimitivesGeneratedQueryShared(contextVk))
{
return nullptr;
}
switch (type)
{
case gl::QueryType::PrimitivesGenerated:
......@@ -80,7 +91,8 @@ QueryVk *GetOnRenderPassStartEndShareQuery(ContextVk *contextVk, gl::QueryType t
gl::QueryType::PrimitivesGenerated < gl::QueryType::TransformFeedbackPrimitivesWritten,
"incorrect assumption about the order in which queries are started in a render pass");
if (type != gl::QueryType::TransformFeedbackPrimitivesWritten)
if (type != gl::QueryType::TransformFeedbackPrimitivesWritten ||
!IsPrimitivesGeneratedQueryShared(contextVk))
{
return nullptr;
}
......@@ -206,7 +218,7 @@ angle::Result QueryVk::accumulateStashedQueryResult(ContextVk *contextVk, vk::Qu
{
for (vk::Shared<vk::QueryHelper> &query : mStashedQueryHelpers)
{
vk::QueryResult v(getQueryResultCount());
vk::QueryResult v(getQueryResultCount(contextVk));
ANGLE_TRY(query.get().getUint64Result(contextVk, &v));
*result += v;
}
......@@ -528,7 +540,7 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
// stashed queries will incur a wait that is not desired by the application.
ASSERT(!IsRenderPassQuery(contextVk, mType) || mQueryHelper.get().hasSubmittedCommands());
vk::QueryResult result(getQueryResultCount());
vk::QueryResult result(getQueryResultCount(contextVk));
if (wait)
{
......@@ -576,10 +588,14 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
}
case gl::QueryType::TransformFeedbackPrimitivesWritten:
mCachedResult =
result.getResult(vk::QueryResult::kTransformFeedbackPrimitivesWrittenIndex);
result.getResult(IsPrimitivesGeneratedQueryShared(contextVk)
? vk::QueryResult::kTransformFeedbackPrimitivesWrittenIndex
: vk::QueryResult::kDefaultResultIndex);
break;
case gl::QueryType::PrimitivesGenerated:
mCachedResult = result.getResult(vk::QueryResult::kPrimitivesGeneratedIndex);
mCachedResult = result.getResult(IsPrimitivesGeneratedQueryShared(contextVk)
? vk::QueryResult::kPrimitivesGeneratedIndex
: vk::QueryResult::kDefaultResultIndex);
break;
default:
UNREACHABLE();
......@@ -630,11 +646,12 @@ void QueryVk::onTransformFeedbackEnd(GLsizeiptr primitivesDrawn)
mTransformFeedbackPrimitivesDrawn += primitivesDrawn;
}
uint32_t QueryVk::getQueryResultCount() const
uint32_t QueryVk::getQueryResultCount(ContextVk *contextVk) const
{
switch (mType)
{
case gl::QueryType::PrimitivesGenerated:
return IsPrimitivesGeneratedQueryShared(contextVk) ? 2 : 1;
case gl::QueryType::TransformFeedbackPrimitivesWritten:
return 2;
default:
......
......@@ -53,7 +53,7 @@ class QueryVk : public QueryImpl
bool isCurrentlyInUse(Serial lastCompletedSerial) const;
angle::Result finishRunningCommands(ContextVk *contextVk);
void stashQueryHelper();
uint32_t getQueryResultCount() const;
uint32_t getQueryResultCount(ContextVk *contextVk) const;
angle::Result accumulateStashedQueryResult(ContextVk *contextVk, vk::QueryResult *result);
// Manage query allocations
......
......@@ -1647,6 +1647,9 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF
// Used to implement storage buffers and images in the fragment shader:
enabledFeatures.features.fragmentStoresAndAtomics =
mPhysicalDeviceFeatures.fragmentStoresAndAtomics;
// Used to emulate the primitives generated query:
enabledFeatures.features.pipelineStatisticsQuery =
getFeatures().supportsPipelineStatisticsQuery.enabled;
// Used to support geometry shaders:
enabledFeatures.features.geometryShader = mPhysicalDeviceFeatures.geometryShader;
// Used to support EXT_gpu_shader5:
......@@ -2433,6 +2436,11 @@ void RendererVk::initFeatures(DisplayVk *displayVk,
mPhysicalDeviceFeatures.imageCubeArray == VK_TRUE && !isSwiftShader &&
!IsPixel2(mPhysicalDeviceProperties.vendorID, mPhysicalDeviceProperties.deviceID));
// TODO: Only enable if VK_EXT_primitives_generated_query is not present.
// http://anglebug.com/5430
ANGLE_FEATURE_CONDITION(&mFeatures, supportsPipelineStatisticsQuery,
mPhysicalDeviceFeatures.pipelineStatisticsQuery == VK_TRUE);
ANGLE_FEATURE_CONDITION(&mFeatures, preferredLargeHeapBlockSize4MB, !isQualcomm);
// Defer glFLush call causes manhattan 3.0 perf regression. Let Qualcomm driver opt out from
......
......@@ -2723,6 +2723,11 @@ angle::Result DynamicQueryPool::allocateNewPool(ContextVk *contextVk)
queryPoolInfo.queryCount = mPoolSize;
queryPoolInfo.pipelineStatistics = 0;
if (mQueryType == VK_QUERY_TYPE_PIPELINE_STATISTICS)
{
queryPoolInfo.pipelineStatistics = VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT;
}
QueryPool queryPool;
ANGLE_VK_TRY(contextVk, queryPool.init(contextVk->getDevice(), queryPoolInfo));
......
......@@ -398,9 +398,10 @@ class DynamicallyGrowingPool : angle::NonCopyable
// another is created. The query pools live permanently, but are recycled as indices get freed.
// These are arbitrary default sizes for query pools.
constexpr uint32_t kDefaultOcclusionQueryPoolSize = 64;
constexpr uint32_t kDefaultTimestampQueryPoolSize = 64;
constexpr uint32_t kDefaultTransformFeedbackQueryPoolSize = 128;
constexpr uint32_t kDefaultOcclusionQueryPoolSize = 64;
constexpr uint32_t kDefaultTimestampQueryPoolSize = 64;
constexpr uint32_t kDefaultTransformFeedbackQueryPoolSize = 128;
constexpr uint32_t kDefaultPrimitivesGeneratedQueryPoolSize = 128;
class QueryHelper;
......
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