blob: 5c7961d513a82be32c61650b8c875764f33e0332 [file] [log] [blame]
//
// Copyright (c) 2013 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Query11.cpp: Defines the rx::Query11 class which implements rx::QueryImpl.
#include "libANGLE/renderer/d3d/d3d11/Query11.h"
#include <GLES2/gl2ext.h>
#include "common/utilities.h"
#include "libANGLE/renderer/d3d/d3d11/Renderer11.h"
#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
namespace
{
GLuint64 MergeQueryResults(GLenum type, GLuint64 currentResult, GLuint64 newResult)
{
switch (type)
{
case GL_ANY_SAMPLES_PASSED:
case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
return (currentResult == GL_TRUE || newResult == GL_TRUE) ? GL_TRUE : GL_FALSE;
case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
return currentResult + newResult;
case GL_TIME_ELAPSED_EXT:
return currentResult + newResult;
case GL_TIMESTAMP_EXT:
return newResult;
case GL_COMMANDS_COMPLETED_CHROMIUM:
return newResult;
default:
UNREACHABLE();
return 0;
}
}
} // anonymous namespace
namespace rx
{
Query11::QueryState::QueryState()
: query(nullptr), beginTimestamp(nullptr), endTimestamp(nullptr), finished(false)
{
}
Query11::QueryState::~QueryState()
{
SafeRelease(beginTimestamp);
SafeRelease(endTimestamp);
SafeRelease(query);
}
Query11::Query11(Renderer11 *renderer, GLenum type)
: QueryImpl(type), mResult(0), mResultSum(0), mRenderer(renderer)
{
mActiveQuery = std::unique_ptr<QueryState>(new QueryState());
}
Query11::~Query11()
{
mRenderer->getStateManager()->onDeleteQueryObject(this);
}
gl::Error Query11::begin()
{
mResultSum = 0;
mRenderer->getStateManager()->onBeginQuery(this);
return resume();
}
gl::Error Query11::end()
{
return pause();
}
gl::Error Query11::queryCounter()
{
// This doesn't do anything for D3D11 as we don't support timestamps
ASSERT(getType() == GL_TIMESTAMP_EXT);
mResultSum = 0;
mPendingQueries.push_back(std::unique_ptr<QueryState>(new QueryState()));
return gl::NoError();
}
template <typename T>
gl::Error Query11::getResultBase(T *params)
{
ASSERT(mActiveQuery->query == nullptr);
ANGLE_TRY(flush(true));
ASSERT(mPendingQueries.empty());
*params = static_cast<T>(mResultSum);
return gl::NoError();
}
gl::Error Query11::getResult(GLint *params)
{
return getResultBase(params);
}
gl::Error Query11::getResult(GLuint *params)
{
return getResultBase(params);
}
gl::Error Query11::getResult(GLint64 *params)
{
return getResultBase(params);
}
gl::Error Query11::getResult(GLuint64 *params)
{
return getResultBase(params);
}
gl::Error Query11::isResultAvailable(bool *available)
{
ANGLE_TRY(flush(false));
*available = mPendingQueries.empty();
return gl::NoError();
}
gl::Error Query11::pause()
{
if (mActiveQuery->query != nullptr)
{
ID3D11DeviceContext *context = mRenderer->getDeviceContext();
GLenum queryType = getType();
// If we are doing time elapsed query the end timestamp
if (queryType == GL_TIME_ELAPSED_EXT)
{
context->End(mActiveQuery->endTimestamp);
}
context->End(mActiveQuery->query);
mPendingQueries.push_back(std::move(mActiveQuery));
mActiveQuery = std::unique_ptr<QueryState>(new QueryState());
}
return flush(false);
}
gl::Error Query11::resume()
{
if (mActiveQuery->query == nullptr)
{
ANGLE_TRY(flush(false));
GLenum queryType = getType();
D3D11_QUERY d3dQueryType = gl_d3d11::ConvertQueryType(queryType);
D3D11_QUERY_DESC queryDesc;
queryDesc.Query = d3dQueryType;
queryDesc.MiscFlags = 0;
ID3D11Device *device = mRenderer->getDevice();
HRESULT result = device->CreateQuery(&queryDesc, &mActiveQuery->query);
if (FAILED(result))
{
return gl::Error(GL_OUT_OF_MEMORY, "Internal query creation failed, result: 0x%X.",
result);
}
// If we are doing time elapsed we also need a query to actually query the timestamp
if (queryType == GL_TIME_ELAPSED_EXT)
{
D3D11_QUERY_DESC desc;
desc.Query = D3D11_QUERY_TIMESTAMP;
desc.MiscFlags = 0;
result = device->CreateQuery(&desc, &mActiveQuery->beginTimestamp);
if (FAILED(result))
{
return gl::Error(GL_OUT_OF_MEMORY, "Internal query creation failed, result: 0x%X.",
result);
}
result = device->CreateQuery(&desc, &mActiveQuery->endTimestamp);
if (FAILED(result))
{
return gl::Error(GL_OUT_OF_MEMORY, "Internal query creation failed, result: 0x%X.",
result);
}
}
ID3D11DeviceContext *context = mRenderer->getDeviceContext();
if (d3dQueryType != D3D11_QUERY_EVENT)
{
context->Begin(mActiveQuery->query);
}
// If we are doing time elapsed, query the begin timestamp
if (queryType == GL_TIME_ELAPSED_EXT)
{
context->End(mActiveQuery->beginTimestamp);
}
}
return gl::NoError();
}
gl::Error Query11::flush(bool force)
{
while (!mPendingQueries.empty())
{
QueryState *query = mPendingQueries.front().get();
do
{
ANGLE_TRY(testQuery(query));
if (!query->finished && !force)
{
return gl::NoError();
}
} while (!query->finished);
mResultSum = MergeQueryResults(getType(), mResultSum, mResult);
mPendingQueries.pop_front();
}
return gl::NoError();
}
gl::Error Query11::testQuery(QueryState *queryState)
{
if (!queryState->finished)
{
ID3D11DeviceContext *context = mRenderer->getDeviceContext();
switch (getType())
{
case GL_ANY_SAMPLES_PASSED_EXT:
case GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT:
{
ASSERT(queryState->query);
UINT64 numPixels = 0;
HRESULT result =
context->GetData(queryState->query, &numPixels, sizeof(numPixels), 0);
if (FAILED(result))
{
return gl::Error(GL_OUT_OF_MEMORY,
"Failed to get the data of an internal query, result: 0x%X.",
result);
}
if (result == S_OK)
{
queryState->finished = true;
mResult = (numPixels > 0) ? GL_TRUE : GL_FALSE;
}
}
break;
case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
{
ASSERT(queryState->query);
D3D11_QUERY_DATA_SO_STATISTICS soStats = {0};
HRESULT result = context->GetData(queryState->query, &soStats, sizeof(soStats), 0);
if (FAILED(result))
{
return gl::Error(GL_OUT_OF_MEMORY,
"Failed to get the data of an internal query, result: 0x%X.",
result);
}
if (result == S_OK)
{
queryState->finished = true;
mResult = static_cast<GLuint64>(soStats.NumPrimitivesWritten);
}
}
break;
case GL_TIME_ELAPSED_EXT:
{
ASSERT(queryState->query);
ASSERT(queryState->beginTimestamp);
ASSERT(queryState->endTimestamp);
D3D11_QUERY_DATA_TIMESTAMP_DISJOINT timeStats = {0};
HRESULT result =
context->GetData(queryState->query, &timeStats, sizeof(timeStats), 0);
if (FAILED(result))
{
return gl::Error(GL_OUT_OF_MEMORY,
"Failed to get the data of an internal query, result: 0x%X.",
result);
}
if (result == S_OK)
{
UINT64 beginTime = 0;
HRESULT beginRes =
context->GetData(queryState->beginTimestamp, &beginTime, sizeof(UINT64), 0);
if (FAILED(beginRes))
{
return gl::Error(
GL_OUT_OF_MEMORY,
"Failed to get the data of an internal query, result: 0x%X.", beginRes);
}
UINT64 endTime = 0;
HRESULT endRes =
context->GetData(queryState->endTimestamp, &endTime, sizeof(UINT64), 0);
if (FAILED(endRes))
{
return gl::Error(
GL_OUT_OF_MEMORY,
"Failed to get the data of an internal query, result: 0x%X.", endRes);
}
if (beginRes == S_OK && endRes == S_OK)
{
queryState->finished = true;
if (timeStats.Disjoint)
{
mRenderer->setGPUDisjoint();
}
static_assert(sizeof(UINT64) == sizeof(unsigned long long),
"D3D UINT64 isn't 64 bits");
angle::CheckedNumeric<UINT64> checkedTime(endTime);
checkedTime -= beginTime;
checkedTime *= 1000000000ull;
checkedTime /= timeStats.Frequency;
if (checkedTime.IsValid())
{
mResult = checkedTime.ValueOrDie();
}
else
{
mResult = std::numeric_limits<GLuint64>::max() / timeStats.Frequency;
// If an overflow does somehow occur, there is no way the elapsed time
// is accurate, so we generate a disjoint event
mRenderer->setGPUDisjoint();
}
}
}
}
break;
case GL_TIMESTAMP_EXT:
{
// D3D11 doesn't support GL timestamp queries as D3D timestamps are not guaranteed
// to have any sort of continuity outside of a disjoint timestamp query block, which
// GL depends on
ASSERT(queryState->query == nullptr);
mResult = 0;
queryState->finished = true;
}
break;
case GL_COMMANDS_COMPLETED_CHROMIUM:
{
ASSERT(queryState->query);
BOOL completed = 0;
HRESULT result =
context->GetData(queryState->query, &completed, sizeof(completed), 0);
if (FAILED(result))
{
return gl::Error(GL_OUT_OF_MEMORY,
"Failed to get the data of an internal query, result: 0x%X.",
result);
}
if (result == S_OK)
{
queryState->finished = true;
ASSERT(completed == TRUE);
mResult = (completed == TRUE) ? GL_TRUE : GL_FALSE;
}
}
break;
default:
UNREACHABLE();
break;
}
if (!queryState->finished && mRenderer->testDeviceLost())
{
mRenderer->notifyDeviceLost();
return gl::Error(GL_OUT_OF_MEMORY, "Failed to test get query result, device is lost.");
}
}
return gl::NoError();
}
} // namespace rx