Commit c2b576d9 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Implement GL_EXT_disjoint_timer_query

- QueryVk::queryCounter() and relevant utils are implemented for the sake of Timestamp queries. - TimeElapsed queries are implemented using two Timestamp queries. Bug: angleproject:2885 Change-Id: Id181bd97f5a24e7e96b3ea1b819483227e64daf0 Reviewed-on: https://chromium-review.googlesource.com/c/1276806 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent c4765aa7
...@@ -65,6 +65,8 @@ const char *GetResourceTypeName(CommandGraphResourceType resourceType, ...@@ -65,6 +65,8 @@ const char *GetResourceTypeName(CommandGraphResourceType resourceType,
return "BeginQuery"; return "BeginQuery";
case CommandGraphNodeFunction::EndQuery: case CommandGraphNodeFunction::EndQuery:
return "EndQuery"; return "EndQuery";
case CommandGraphNodeFunction::WriteTimestamp:
return "WriteTimestamp";
default: default:
UNREACHABLE(); UNREACHABLE();
return "Query"; return "Query";
...@@ -196,6 +198,14 @@ void CommandGraphResource::endQuery(Context *context, ...@@ -196,6 +198,14 @@ void CommandGraphResource::endQuery(Context *context,
mCurrentWritingNode->setQueryPool(queryPool, queryIndex); mCurrentWritingNode->setQueryPool(queryPool, queryIndex);
} }
void CommandGraphResource::writeTimestamp(Context *context,
const QueryPool *queryPool,
uint32_t queryIndex)
{
startNewCommands(context->getRenderer(), CommandGraphNodeFunction::WriteTimestamp);
mCurrentWritingNode->setQueryPool(queryPool, queryIndex);
}
void CommandGraphResource::finishCurrentCommands(RendererVk *renderer) void CommandGraphResource::finishCurrentCommands(RendererVk *renderer)
{ {
startNewCommands(renderer, CommandGraphNodeFunction::Generic); startNewCommands(renderer, CommandGraphNodeFunction::Generic);
...@@ -387,7 +397,8 @@ bool CommandGraphNode::hasParents() const ...@@ -387,7 +397,8 @@ bool CommandGraphNode::hasParents() const
void CommandGraphNode::setQueryPool(const QueryPool *queryPool, uint32_t queryIndex) void CommandGraphNode::setQueryPool(const QueryPool *queryPool, uint32_t queryIndex)
{ {
ASSERT(mFunction == CommandGraphNodeFunction::BeginQuery || ASSERT(mFunction == CommandGraphNodeFunction::BeginQuery ||
mFunction == CommandGraphNodeFunction::EndQuery); mFunction == CommandGraphNodeFunction::EndQuery ||
mFunction == CommandGraphNodeFunction::WriteTimestamp);
mQueryPool = queryPool->getHandle(); mQueryPool = queryPool->getHandle();
mQueryIndex = queryIndex; mQueryIndex = queryIndex;
} }
...@@ -496,6 +507,16 @@ angle::Result CommandGraphNode::visitAndExecute(vk::Context *context, ...@@ -496,6 +507,16 @@ angle::Result CommandGraphNode::visitAndExecute(vk::Context *context,
break; break;
case CommandGraphNodeFunction::WriteTimestamp:
ASSERT(!mOutsideRenderPassCommands.valid() && !mInsideRenderPassCommands.valid());
ASSERT(mQueryPool != VK_NULL_HANDLE);
primaryCommandBuffer->resetQueryPool(mQueryPool, mQueryIndex, 1);
primaryCommandBuffer->writeTimestamp(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mQueryPool,
mQueryIndex);
break;
default: default:
UNREACHABLE(); UNREACHABLE();
} }
......
...@@ -39,6 +39,7 @@ enum class CommandGraphNodeFunction ...@@ -39,6 +39,7 @@ enum class CommandGraphNodeFunction
Generic, Generic,
BeginQuery, BeginQuery,
EndQuery, EndQuery,
WriteTimestamp,
}; };
// Only used internally in the command graph. Kept in the header for better inlining performance. // Only used internally in the command graph. Kept in the header for better inlining performance.
...@@ -182,6 +183,7 @@ class CommandGraphResource : angle::NonCopyable ...@@ -182,6 +183,7 @@ class CommandGraphResource : angle::NonCopyable
void beginQuery(Context *context, const QueryPool *queryPool, uint32_t queryIndex); void beginQuery(Context *context, const QueryPool *queryPool, uint32_t queryIndex);
void endQuery(Context *context, const QueryPool *queryPool, uint32_t queryIndex); void endQuery(Context *context, const QueryPool *queryPool, uint32_t queryIndex);
void writeTimestamp(Context *context, const QueryPool *queryPool, uint32_t queryIndex);
// Checks if we're in a RenderPass, returning true if so. Updates serial internally. // Checks if we're in a RenderPass, returning true if so. Updates serial internally.
// Returns the started command buffer in commandBufferOut. // Returns the started command buffer in commandBufferOut.
......
...@@ -200,6 +200,10 @@ gl::Error ContextVk::initialize() ...@@ -200,6 +200,10 @@ gl::Error ContextVk::initialize()
vk::kDefaultOcclusionQueryPoolSize)); vk::kDefaultOcclusionQueryPoolSize));
ANGLE_TRY(mQueryPools[gl::QueryType::AnySamplesConservative].init( ANGLE_TRY(mQueryPools[gl::QueryType::AnySamplesConservative].init(
this, VK_QUERY_TYPE_OCCLUSION, vk::kDefaultOcclusionQueryPoolSize)); this, VK_QUERY_TYPE_OCCLUSION, vk::kDefaultOcclusionQueryPoolSize));
ANGLE_TRY(mQueryPools[gl::QueryType::Timestamp].init(this, VK_QUERY_TYPE_TIMESTAMP,
vk::kDefaultTimestampQueryPoolSize));
ANGLE_TRY(mQueryPools[gl::QueryType::TimeElapsed].init(this, VK_QUERY_TYPE_TIMESTAMP,
vk::kDefaultTimestampQueryPoolSize));
// TODO(syoussefi): Initialize other query pools as they get implemented. // TODO(syoussefi): Initialize other query pools as they get implemented.
size_t minAlignment = static_cast<size_t>( size_t minAlignment = static_cast<size_t>(
...@@ -1083,7 +1087,8 @@ vk::DynamicDescriptorPool *ContextVk::getDynamicDescriptorPool(uint32_t descript ...@@ -1083,7 +1087,8 @@ vk::DynamicDescriptorPool *ContextVk::getDynamicDescriptorPool(uint32_t descript
vk::DynamicQueryPool *ContextVk::getQueryPool(gl::QueryType queryType) vk::DynamicQueryPool *ContextVk::getQueryPool(gl::QueryType queryType)
{ {
ASSERT(queryType == gl::QueryType::AnySamples || ASSERT(queryType == gl::QueryType::AnySamples ||
queryType == gl::QueryType::AnySamplesConservative); queryType == gl::QueryType::AnySamplesConservative ||
queryType == gl::QueryType::Timestamp || queryType == gl::QueryType::TimeElapsed);
ASSERT(mQueryPools[queryType].isValid()); ASSERT(mQueryPools[queryType].isValid());
return &mQueryPools[queryType]; return &mQueryPools[queryType];
} }
......
...@@ -27,6 +27,7 @@ gl::Error QueryVk::onDestroy(const gl::Context *context) ...@@ -27,6 +27,7 @@ gl::Error QueryVk::onDestroy(const gl::Context *context)
{ {
ContextVk *contextVk = vk::GetImpl(context); ContextVk *contextVk = vk::GetImpl(context);
contextVk->getQueryPool(getType())->freeQuery(contextVk, &mQueryHelper); contextVk->getQueryPool(getType())->freeQuery(contextVk, &mQueryHelper);
contextVk->getQueryPool(getType())->freeQuery(contextVk, &mQueryHelperTimeElapsedBegin);
return gl::NoError(); return gl::NoError();
} }
...@@ -35,11 +36,30 @@ gl::Error QueryVk::begin(const gl::Context *context) ...@@ -35,11 +36,30 @@ gl::Error QueryVk::begin(const gl::Context *context)
{ {
ContextVk *contextVk = vk::GetImpl(context); ContextVk *contextVk = vk::GetImpl(context);
mCachedResultValid = false;
if (!mQueryHelper.getQueryPool())
{
ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(contextVk, &mQueryHelper)); ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(contextVk, &mQueryHelper));
}
mCachedResultValid = false; // Note: TimeElapsed is implemented by using two Timestamp queries and taking the diff.
if (getType() == gl::QueryType::TimeElapsed)
{
if (!mQueryHelperTimeElapsedBegin.getQueryPool())
{
ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(
contextVk, &mQueryHelperTimeElapsedBegin));
}
mQueryHelperTimeElapsedBegin.writeTimestamp(contextVk,
mQueryHelperTimeElapsedBegin.getQueryPool(),
mQueryHelperTimeElapsedBegin.getQuery());
}
else
{
mQueryHelper.beginQuery(contextVk, mQueryHelper.getQueryPool(), mQueryHelper.getQuery()); mQueryHelper.beginQuery(contextVk, mQueryHelper.getQueryPool(), mQueryHelper.getQuery());
}
return gl::NoError(); return gl::NoError();
} }
...@@ -48,15 +68,35 @@ gl::Error QueryVk::end(const gl::Context *context) ...@@ -48,15 +68,35 @@ gl::Error QueryVk::end(const gl::Context *context)
{ {
ContextVk *contextVk = vk::GetImpl(context); ContextVk *contextVk = vk::GetImpl(context);
if (getType() == gl::QueryType::TimeElapsed)
{
mQueryHelper.writeTimestamp(contextVk, mQueryHelper.getQueryPool(),
mQueryHelper.getQuery());
}
else
{
mQueryHelper.endQuery(contextVk, mQueryHelper.getQueryPool(), mQueryHelper.getQuery()); mQueryHelper.endQuery(contextVk, mQueryHelper.getQueryPool(), mQueryHelper.getQuery());
}
return gl::NoError(); return gl::NoError();
} }
gl::Error QueryVk::queryCounter(const gl::Context *context) gl::Error QueryVk::queryCounter(const gl::Context *context)
{ {
UNIMPLEMENTED(); ContextVk *contextVk = vk::GetImpl(context);
return gl::InternalError();
mCachedResultValid = false;
if (!mQueryHelper.getQueryPool())
{
ANGLE_TRY(contextVk->getQueryPool(getType())->allocateQuery(contextVk, &mQueryHelper));
}
ASSERT(getType() == gl::QueryType::Timestamp);
mQueryHelper.writeTimestamp(contextVk, mQueryHelper.getQueryPool(), mQueryHelper.getQuery());
return gl::NoError();
} }
angle::Result QueryVk::getResult(const gl::Context *context, bool wait) angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
...@@ -71,9 +111,13 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait) ...@@ -71,9 +111,13 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
// glGetQueryObject* requires an implicit flush of the command buffers to guarantee execution in // glGetQueryObject* requires an implicit flush of the command buffers to guarantee execution in
// finite time. // finite time.
// Note regarding time-elapsed: end should have been called after begin, so flushing when end
// has pending work should flush begin too.
if (mQueryHelper.hasPendingWork(renderer)) if (mQueryHelper.hasPendingWork(renderer))
{ {
ANGLE_TRY_HANDLE(context, renderer->flush(contextVk)); ANGLE_TRY_HANDLE(context, renderer->flush(contextVk));
ASSERT(!mQueryHelperTimeElapsedBegin.hasPendingWork(renderer));
ASSERT(!mQueryHelper.hasPendingWork(renderer)); ASSERT(!mQueryHelper.hasPendingWork(renderer));
} }
...@@ -113,6 +157,24 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait) ...@@ -113,6 +157,24 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
// OpenGL query result in these cases is binary // OpenGL query result in these cases is binary
mCachedResult = !!mCachedResult; mCachedResult = !!mCachedResult;
break; break;
case gl::QueryType::Timestamp:
break;
case gl::QueryType::TimeElapsed:
{
uint64_t timeElapsedEnd = mCachedResult;
result = mQueryHelperTimeElapsedBegin.getQueryPool()->getResults(
contextVk, mQueryHelperTimeElapsedBegin.getQuery(), 1, sizeof(mCachedResult),
&mCachedResult, sizeof(mCachedResult), flags);
ANGLE_TRY(result);
// Since the result of the end query of time-elapsed is already available, the
// result of begin query must be available too.
ASSERT(result != angle::Result::Incomplete());
mCachedResult = timeElapsedEnd - mCachedResult;
break;
}
default: default:
UNREACHABLE(); UNREACHABLE();
break; break;
...@@ -121,7 +183,6 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait) ...@@ -121,7 +183,6 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
mCachedResultValid = true; mCachedResultValid = true;
return angle::Result::Continue(); return angle::Result::Continue();
} }
gl::Error QueryVk::getResult(const gl::Context *context, GLint *params) gl::Error QueryVk::getResult(const gl::Context *context, GLint *params)
{ {
ANGLE_TRY(getResult(context, true)); ANGLE_TRY(getResult(context, true));
......
...@@ -36,7 +36,10 @@ class QueryVk : public QueryImpl ...@@ -36,7 +36,10 @@ class QueryVk : public QueryImpl
private: private:
angle::Result getResult(const gl::Context *context, bool wait); angle::Result getResult(const gl::Context *context, bool wait);
// Used for AnySamples, AnySamplesConservative, Timestamp and TimeElapsed (end)
vk::QueryHelper mQueryHelper; vk::QueryHelper mQueryHelper;
// An additional query used for TimeElapsed (begin), as it is implemented using Timestamp
vk::QueryHelper mQueryHelperTimeElapsedBegin;
uint64_t mCachedResult; uint64_t mCachedResult;
bool mCachedResultValid; bool mCachedResultValid;
}; };
......
...@@ -769,7 +769,9 @@ void RendererVk::ensureCapsInitialized() const ...@@ -769,7 +769,9 @@ void RendererVk::ensureCapsInitialized() const
{ {
if (!mCapsInitialized) if (!mCapsInitialized)
{ {
vk::GenerateCaps(mPhysicalDeviceProperties, mPhysicalDeviceFeatures, mNativeTextureCaps, ASSERT(mCurrentQueueFamilyIndex < mQueueFamilyProperties.size());
vk::GenerateCaps(mPhysicalDeviceProperties, mPhysicalDeviceFeatures,
mQueueFamilyProperties[mCurrentQueueFamilyIndex], mNativeTextureCaps,
&mNativeCaps, &mNativeExtensions, &mNativeLimitations); &mNativeCaps, &mNativeExtensions, &mNativeLimitations);
mCapsInitialized = true; mCapsInitialized = true;
} }
......
...@@ -31,6 +31,7 @@ namespace vk ...@@ -31,6 +31,7 @@ namespace vk
void GenerateCaps(const VkPhysicalDeviceProperties &physicalDeviceProperties, void GenerateCaps(const VkPhysicalDeviceProperties &physicalDeviceProperties,
const VkPhysicalDeviceFeatures &physicalDeviceFeatures, const VkPhysicalDeviceFeatures &physicalDeviceFeatures,
const VkQueueFamilyProperties &queueFamilyProperties,
const gl::TextureCapsMap &textureCaps, const gl::TextureCapsMap &textureCaps,
gl::Caps *outCaps, gl::Caps *outCaps,
gl::Extensions *outExtensions, gl::Extensions *outExtensions,
...@@ -53,6 +54,15 @@ void GenerateCaps(const VkPhysicalDeviceProperties &physicalDeviceProperties, ...@@ -53,6 +54,15 @@ void GenerateCaps(const VkPhysicalDeviceProperties &physicalDeviceProperties,
// unless that feature is available. // unless that feature is available.
outExtensions->occlusionQueryBoolean = physicalDeviceFeatures.inheritedQueries; outExtensions->occlusionQueryBoolean = physicalDeviceFeatures.inheritedQueries;
// From the Vulkan specs:
// > The number of valid bits in a timestamp value is determined by the
// > VkQueueFamilyProperties::timestampValidBits property of the queue on which the timestamp is
// > written. Timestamps are supported on any queue which reports a non-zero value for
// > timestampValidBits via vkGetPhysicalDeviceQueueFamilyProperties.
outExtensions->disjointTimerQuery = queueFamilyProperties.timestampValidBits > 0;
outExtensions->queryCounterBitsTimeElapsed = queueFamilyProperties.timestampValidBits;
outExtensions->queryCounterBitsTimestamp = queueFamilyProperties.timestampValidBits;
// TODO(lucferron): Eventually remove everything above this line in this function as the caps // TODO(lucferron): Eventually remove everything above this line in this function as the caps
// get implemented. // get implemented.
// https://vulkan.lunarg.com/doc/view/1.0.30.0/linux/vkspec.chunked/ch31s02.html // https://vulkan.lunarg.com/doc/view/1.0.30.0/linux/vkspec.chunked/ch31s02.html
......
...@@ -34,6 +34,7 @@ namespace vk ...@@ -34,6 +34,7 @@ namespace vk
{ {
void GenerateCaps(const VkPhysicalDeviceProperties &physicalDeviceProperties, void GenerateCaps(const VkPhysicalDeviceProperties &physicalDeviceProperties,
const VkPhysicalDeviceFeatures &physicalDeviceFeatures, const VkPhysicalDeviceFeatures &physicalDeviceFeatures,
const VkQueueFamilyProperties &queueFamilyProperties,
const gl::TextureCapsMap &textureCaps, const gl::TextureCapsMap &textureCaps,
gl::Caps *outCaps, gl::Caps *outCaps,
gl::Extensions *outExtensions, gl::Extensions *outExtensions,
......
...@@ -211,8 +211,9 @@ class DynamicallyGrowingPool : angle::NonCopyable ...@@ -211,8 +211,9 @@ class DynamicallyGrowingPool : angle::NonCopyable
// DynamicQueryPool allocates indices out of QueryPool as needed. Once a QueryPool is exhausted, // DynamicQueryPool allocates indices out of QueryPool as needed. Once a QueryPool is exhausted,
// another is created. The query pools live permanently, but are recycled as indices get freed. // another is created. The query pools live permanently, but are recycled as indices get freed.
// This is an arbitrary default size for occlusion query pools. // These are arbitrary default sizes for query pools.
constexpr uint32_t kDefaultOcclusionQueryPoolSize = 64; constexpr uint32_t kDefaultOcclusionQueryPoolSize = 64;
constexpr uint32_t kDefaultTimestampQueryPoolSize = 64;
class QueryHelper; class QueryHelper;
......
...@@ -583,6 +583,14 @@ void CommandBuffer::endQuery(VkQueryPool queryPool, uint32_t query) ...@@ -583,6 +583,14 @@ void CommandBuffer::endQuery(VkQueryPool queryPool, uint32_t query)
vkCmdEndQuery(mHandle, queryPool, query); vkCmdEndQuery(mHandle, queryPool, query);
} }
void CommandBuffer::writeTimestamp(VkPipelineStageFlagBits pipelineStage,
VkQueryPool queryPool,
uint32_t query)
{
ASSERT(valid());
vkCmdWriteTimestamp(mHandle, pipelineStage, queryPool, query);
}
// Image implementation. // Image implementation.
Image::Image() Image::Image()
{ {
......
...@@ -427,6 +427,9 @@ class CommandBuffer : public WrappedObject<CommandBuffer, VkCommandBuffer> ...@@ -427,6 +427,9 @@ class CommandBuffer : public WrappedObject<CommandBuffer, VkCommandBuffer>
void resetQueryPool(VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount); void resetQueryPool(VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount);
void beginQuery(VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags); void beginQuery(VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags);
void endQuery(VkQueryPool queryPool, uint32_t query); void endQuery(VkQueryPool queryPool, uint32_t query);
void writeTimestamp(VkPipelineStageFlagBits pipelineStage,
VkQueryPool queryPool,
uint32_t query);
}; };
class Image final : public WrappedObject<Image, VkImage> class Image final : public WrappedObject<Image, VkImage>
......
...@@ -157,6 +157,12 @@ TEST_P(TimerQueriesTest, TimeElapsed) ...@@ -157,6 +157,12 @@ TEST_P(TimerQueriesTest, TimeElapsed)
EXPECT_LT(0ul, result1); EXPECT_LT(0ul, result1);
EXPECT_LT(0ul, result2); EXPECT_LT(0ul, result2);
// The time elapsed should be less than a second. Not an actual
// requirement, but longer than a second to draw something basic hints at
// an issue with the queries themselves.
EXPECT_LT(result1, 1000000000ul);
EXPECT_LT(result2, 1000000000ul);
// TODO(geofflang): Re-enable this check when it is non-flaky // TODO(geofflang): Re-enable this check when it is non-flaky
// The costly quad should take longer than the cheap quad // The costly quad should take longer than the cheap quad
// EXPECT_LT(result1, result2); // EXPECT_LT(result1, result2);
...@@ -216,6 +222,9 @@ TEST_P(TimerQueriesTest, TimeElapsedTextureTest) ...@@ -216,6 +222,9 @@ TEST_P(TimerQueriesTest, TimeElapsedTextureTest)
std::cout << "Elapsed time: " << result << std::endl; std::cout << "Elapsed time: " << result << std::endl;
EXPECT_LT(0ul, result); EXPECT_LT(0ul, result);
// an issue with the queries themselves.
EXPECT_LT(result, 1000000000ul);
} }
// Tests validation of query functions with respect to elapsed time query // Tests validation of query functions with respect to elapsed time query
...@@ -270,6 +279,10 @@ TEST_P(TimerQueriesTest, TimeElapsedMulticontextTest) ...@@ -270,6 +279,10 @@ TEST_P(TimerQueriesTest, TimeElapsedMulticontextTest)
ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_disjoint_timer_query")); ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_disjoint_timer_query"));
// Test skipped because the Vulkan backend doesn't account for (and remove) time spent in other
// contexts.
ANGLE_SKIP_TEST_IF(IsVulkan());
GLint queryTimeElapsedBits = 0; GLint queryTimeElapsedBits = 0;
glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits); glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
...@@ -393,6 +406,8 @@ TEST_P(TimerQueriesTest, TimeElapsedMulticontextTest) ...@@ -393,6 +406,8 @@ TEST_P(TimerQueriesTest, TimeElapsedMulticontextTest)
std::cout << "Elapsed time: " << result2 << " costly quad" << std::endl; std::cout << "Elapsed time: " << result2 << " costly quad" << std::endl;
EXPECT_LT(0ul, result1); EXPECT_LT(0ul, result1);
EXPECT_LT(0ul, result2); EXPECT_LT(0ul, result2);
EXPECT_LT(result1, 1000000000ul);
EXPECT_LT(result2, 1000000000ul);
EXPECT_LT(result1, result2); EXPECT_LT(result1, result2);
} }
...@@ -495,6 +510,7 @@ ANGLE_INSTANTIATE_TEST(TimerQueriesTest, ...@@ -495,6 +510,7 @@ ANGLE_INSTANTIATE_TEST(TimerQueriesTest,
ES2_D3D11(), ES2_D3D11(),
ES3_D3D11(), ES3_D3D11(),
ES2_OPENGL(), ES2_OPENGL(),
ES3_OPENGL()); ES3_OPENGL(),
ES2_VULKAN());
ANGLE_INSTANTIATE_TEST(TimerQueriesTestES3, ES3_D3D11(), ES3_OPENGL()); ANGLE_INSTANTIATE_TEST(TimerQueriesTestES3, ES3_D3D11(), ES3_OPENGL());
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