// // Copyright 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. // // Fence11.cpp: Defines the rx::FenceNV11 and rx::Sync11 classes which implement // rx::FenceNVImpl and rx::SyncImpl. #include "libANGLE/renderer/d3d/d3d11/Fence11.h" #include "common/utilities.h" #include "libANGLE/Context.h" #include "libANGLE/renderer/d3d/d3d11/Context11.h" #include "libANGLE/renderer/d3d/d3d11/Renderer11.h" namespace rx { // // Template helpers for set and test operations. // template angle::Result FenceSetHelper(const gl::Context *context, FenceClass *fence) { if (!fence->mQuery) { D3D11_QUERY_DESC queryDesc; queryDesc.Query = D3D11_QUERY_EVENT; queryDesc.MiscFlags = 0; Context11 *context11 = GetImplAs(context); HRESULT result = fence->mRenderer->getDevice()->CreateQuery(&queryDesc, &fence->mQuery); ANGLE_TRY_HR(context11, result, "Failed to create event query"); } fence->mRenderer->getDeviceContext()->End(fence->mQuery); return angle::Result::Continue; } template angle::Result FenceTestHelper(const gl::Context *context, FenceClass *fence, bool flushCommandBuffer, GLboolean *outFinished) { ASSERT(fence->mQuery); UINT getDataFlags = (flushCommandBuffer ? 0 : D3D11_ASYNC_GETDATA_DONOTFLUSH); Context11 *context11 = GetImplAs(context); HRESULT result = fence->mRenderer->getDeviceContext()->GetData(fence->mQuery, nullptr, 0, getDataFlags); ANGLE_TRY_HR(context11, result, "Failed to get query data"); ASSERT(result == S_OK || result == S_FALSE); *outFinished = ((result == S_OK) ? GL_TRUE : GL_FALSE); return angle::Result::Continue; } // // FenceNV11 // FenceNV11::FenceNV11(Renderer11 *renderer) : FenceNVImpl(), mRenderer(renderer), mQuery(nullptr) {} FenceNV11::~FenceNV11() { SafeRelease(mQuery); } angle::Result FenceNV11::set(const gl::Context *context, GLenum condition) { return FenceSetHelper(context, this); } angle::Result FenceNV11::test(const gl::Context *context, GLboolean *outFinished) { return FenceTestHelper(context, this, true, outFinished); } angle::Result FenceNV11::finish(const gl::Context *context) { GLboolean finished = GL_FALSE; int loopCount = 0; while (finished != GL_TRUE) { loopCount++; ANGLE_TRY(FenceTestHelper(context, this, true, &finished)); bool checkDeviceLost = (loopCount % kPollingD3DDeviceLostCheckFrequency) == 0; if (checkDeviceLost && mRenderer->testDeviceLost()) { ANGLE_TRY_HR(GetImplAs(context), DXGI_ERROR_DRIVER_INTERNAL_ERROR, "Device was lost while querying result of an event query."); } ScheduleYield(); } return angle::Result::Continue; } // // Sync11 // // Important note on accurate timers in Windows: // // QueryPerformanceCounter has a few major issues, including being 10x as expensive to call // as timeGetTime on laptops and "jumping" during certain hardware events. // // See the comments at the top of the Chromium source file "chromium/src/base/time/time_win.cc" // https://code.google.com/p/chromium/codesearch#chromium/src/base/time/time_win.cc // // We still opt to use QPC. In the present and moving forward, most newer systems will not suffer // from buggy implementations. Sync11::Sync11(Renderer11 *renderer) : SyncImpl(), mRenderer(renderer), mQuery(nullptr) { LARGE_INTEGER counterFreqency = {}; BOOL success = QueryPerformanceFrequency(&counterFreqency); ASSERT(success); mCounterFrequency = counterFreqency.QuadPart; } Sync11::~Sync11() { SafeRelease(mQuery); } angle::Result Sync11::set(const gl::Context *context, GLenum condition, GLbitfield flags) { ASSERT(condition == GL_SYNC_GPU_COMMANDS_COMPLETE && flags == 0); return FenceSetHelper(context, this); } angle::Result Sync11::clientWait(const gl::Context *context, GLbitfield flags, GLuint64 timeout, GLenum *outResult) { ASSERT(outResult); bool flushCommandBuffer = ((flags & GL_SYNC_FLUSH_COMMANDS_BIT) != 0); *outResult = GL_WAIT_FAILED; GLboolean result = GL_FALSE; ANGLE_TRY(FenceTestHelper(context, this, flushCommandBuffer, &result)); if (result == GL_TRUE) { *outResult = GL_ALREADY_SIGNALED; return angle::Result::Continue; } if (timeout == 0) { *outResult = GL_TIMEOUT_EXPIRED; return angle::Result::Continue; } LARGE_INTEGER currentCounter = {}; BOOL success = QueryPerformanceCounter(¤tCounter); ASSERT(success); LONGLONG timeoutInSeconds = static_cast(timeout / 1000000000ull); LONGLONG endCounter = currentCounter.QuadPart + mCounterFrequency * timeoutInSeconds; // Extremely unlikely, but if mCounterFrequency is large enough, endCounter can wrap if (endCounter < currentCounter.QuadPart) { endCounter = MAXLONGLONG; } int loopCount = 0; while (currentCounter.QuadPart < endCounter && !result) { loopCount++; ScheduleYield(); success = QueryPerformanceCounter(¤tCounter); ASSERT(success); *outResult = GL_WAIT_FAILED; ANGLE_TRY(FenceTestHelper(context, this, flushCommandBuffer, &result)); bool checkDeviceLost = (loopCount % kPollingD3DDeviceLostCheckFrequency) == 0; if (checkDeviceLost && mRenderer->testDeviceLost()) { *outResult = GL_WAIT_FAILED; ANGLE_TRY_HR(GetImplAs(context), DXGI_ERROR_DRIVER_INTERNAL_ERROR, "Device was lost while querying result of an event query."); } } if (currentCounter.QuadPart >= endCounter) { *outResult = GL_TIMEOUT_EXPIRED; } else { *outResult = GL_CONDITION_SATISFIED; } return angle::Result::Continue; } angle::Result Sync11::serverWait(const gl::Context *context, GLbitfield flags, GLuint64 timeout) { // Because our API is currently designed to be called from a single thread, we don't need to do // extra work for a server-side fence. GPU commands issued after the fence is created will // always be processed after the fence is signaled. return angle::Result::Continue; } angle::Result Sync11::getStatus(const gl::Context *context, GLint *outResult) { GLboolean result = GL_FALSE; // The spec does not specify any way to report errors during the status test (e.g. device // lost) so we report the fence is unblocked in case of error or signaled. *outResult = GL_SIGNALED; ANGLE_TRY(FenceTestHelper(context, this, false, &result)); *outResult = (result ? GL_SIGNALED : GL_UNSIGNALED); return angle::Result::Continue; } } // namespace rx