Branch :
//
// Copyright (c) 2002-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.
//
// Fence.cpp: Implements the gl::Fence class, which supports the GL_NV_fence extension.
// 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.
#include "libGLESv2/Fence.h"
#include "libGLESv2/renderer/FenceImpl.h"
#include "libGLESv2/renderer/Renderer.h"
#include "libGLESv2/main.h"
#include "angle_gl.h"
namespace gl
{
FenceNV::FenceNV(rx::Renderer *renderer)
: mFence(renderer->createFence()),
mIsSet(false),
mStatus(GL_FALSE),
mCondition(GL_NONE)
{
}
FenceNV::~FenceNV()
{
SafeDelete(mFence);
}
GLboolean FenceNV::isFence() const
{
// GL_NV_fence spec:
// A name returned by GenFencesNV, but not yet set via SetFenceNV, is not the name of an existing fence.
return (mIsSet ? GL_TRUE : GL_FALSE);
}
Error FenceNV::setFence(GLenum condition)
{
Error error = mFence->set();
if (error.isError())
{
return error;
}
mCondition = condition;
mStatus = GL_FALSE;
mIsSet = true;
return Error(GL_NO_ERROR);
}
Error FenceNV::testFence(GLboolean *outResult)
{
// Flush the command buffer by default
Error error = mFence->test(true, &mStatus);
if (error.isError())
{
return error;
}
*outResult = mStatus;
return Error(GL_NO_ERROR);
}
Error FenceNV::finishFence()
{
ASSERT(mIsSet);
while (mStatus != GL_TRUE)
{
Error error = mFence->test(true, &mStatus);
if (error.isError())
{
return error;
}
Sleep(0);
}
return Error(GL_NO_ERROR);
}
Error FenceNV::getFencei(GLenum pname, GLint *params)
{
ASSERT(mIsSet);
switch (pname)
{
case GL_FENCE_STATUS_NV:
// GL_NV_fence spec:
// Once the status of a fence has been finished (via FinishFenceNV) or tested and the returned status is TRUE (via either TestFenceNV
// or GetFenceivNV querying the FENCE_STATUS_NV), the status remains TRUE until the next SetFenceNV of the fence.
if (mStatus != GL_TRUE)
{
Error error = mFence->test(false, &mStatus);
if (error.isError())
{
return error;
}
}
*params = mStatus;
break;
case GL_FENCE_CONDITION_NV:
*params = mCondition;
break;
default:
UNREACHABLE();
return gl::Error(GL_INVALID_OPERATION);
}
return Error(GL_NO_ERROR);
}
FenceSync::FenceSync(rx::Renderer *renderer, GLuint id)
: RefCountObject(id),
mFence(renderer->createFence()),
mCounterFrequency(0),
mCondition(GL_NONE)
{
LARGE_INTEGER counterFreqency = { 0 };
BOOL success = QueryPerformanceFrequency(&counterFreqency);
UNUSED_ASSERTION_VARIABLE(success);
ASSERT(success);
mCounterFrequency = counterFreqency.QuadPart;
}
FenceSync::~FenceSync()
{
SafeDelete(mFence);
}
Error FenceSync::set(GLenum condition)
{
Error error = mFence->set();
if (error.isError())
{
return error;
}
mCondition = condition;
return Error(GL_NO_ERROR);
}
Error FenceSync::clientWait(GLbitfield flags, GLuint64 timeout, GLenum *outResult)
{
ASSERT(mCondition != GL_NONE);
bool flushCommandBuffer = ((flags & GL_SYNC_FLUSH_COMMANDS_BIT) != 0);
GLboolean result = GL_FALSE;
Error error = mFence->test(flushCommandBuffer, &result);
if (error.isError())
{
*outResult = GL_WAIT_FAILED;
return error;
}
if (result == GL_TRUE)
{
*outResult = GL_ALREADY_SIGNALED;
return Error(GL_NO_ERROR);
}
if (timeout == 0)
{
*outResult = GL_TIMEOUT_EXPIRED;
return Error(GL_NO_ERROR);
}
LARGE_INTEGER currentCounter = { 0 };
BOOL success = QueryPerformanceCounter(¤tCounter);
UNUSED_ASSERTION_VARIABLE(success);
ASSERT(success);
LONGLONG timeoutInSeconds = static_cast<LONGLONG>(timeout) * static_cast<LONGLONG>(1000000ll);
LONGLONG endCounter = currentCounter.QuadPart + mCounterFrequency * timeoutInSeconds;
while (currentCounter.QuadPart < endCounter && !result)
{
Sleep(0);
BOOL success = QueryPerformanceCounter(¤tCounter);
UNUSED_ASSERTION_VARIABLE(success);
ASSERT(success);
error = mFence->test(flushCommandBuffer, &result);
if (error.isError())
{
*outResult = GL_WAIT_FAILED;
return error;
}
}
if (currentCounter.QuadPart >= endCounter)
{
*outResult = GL_TIMEOUT_EXPIRED;
}
else
{
*outResult = GL_CONDITION_SATISFIED;
}
return Error(GL_NO_ERROR);
}
Error FenceSync::serverWait()
{
// 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 Error(GL_NO_ERROR);
}
Error FenceSync::getStatus(GLint *outResult) const
{
GLboolean result = GL_FALSE;
Error error = mFence->test(false, &result);
if (error.isError())
{
// 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;
return error;
}
*outResult = (result ? GL_SIGNALED : GL_UNSIGNALED);
return Error(GL_NO_ERROR);
}
}