1//
2// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7// Fence.cpp: Implements the gl::Fence class, which supports the GL_NV_fence extension.
8
9// Important note on accurate timers in Windows:
10//
11// QueryPerformanceCounter has a few major issues, including being 10x as expensive to call
12// as timeGetTime on laptops and "jumping" during certain hardware events.
13//
14// See the comments at the top of the Chromium source file "chromium/src/base/time/time_win.cc"
15//   https://code.google.com/p/chromium/codesearch#chromium/src/base/time/time_win.cc
16//
17// We still opt to use QPC. In the present and moving forward, most newer systems will not suffer
18// from buggy implementations.
19
20#include "libGLESv2/Fence.h"
21#include "libGLESv2/renderer/FenceImpl.h"
22#include "libGLESv2/renderer/Renderer.h"
23#include "libGLESv2/main.h"
24
25#include "angle_gl.h"
26
27namespace gl
28{
29
30FenceNV::FenceNV(rx::Renderer *renderer)
31{
32    mFence = renderer->createFence();
33}
34
35FenceNV::~FenceNV()
36{
37    delete mFence;
38}
39
40GLboolean FenceNV::isFence() const
41{
42    // GL_NV_fence spec:
43    // A name returned by GenFencesNV, but not yet set via SetFenceNV, is not the name of an existing fence.
44    return (mFence->isSet() ? GL_TRUE : GL_FALSE);
45}
46
47void FenceNV::setFence(GLenum condition)
48{
49    mFence->set();
50
51    mCondition = condition;
52    mStatus = GL_FALSE;
53}
54
55GLboolean FenceNV::testFence()
56{
57    // Flush the command buffer by default
58    bool result = mFence->test(true);
59
60    mStatus = (result ? GL_TRUE : GL_FALSE);
61    return mStatus;
62}
63
64void FenceNV::finishFence()
65{
66    ASSERT(mFence->isSet());
67
68    while (!mFence->test(true))
69    {
70        Sleep(0);
71    }
72}
73
74GLint FenceNV::getFencei(GLenum pname)
75{
76    ASSERT(mFence->isSet());
77
78    switch (pname)
79    {
80      case GL_FENCE_STATUS_NV:
81        {
82            // GL_NV_fence spec:
83            // Once the status of a fence has been finished (via FinishFenceNV) or tested and the returned status is TRUE (via either TestFenceNV
84            // or GetFenceivNV querying the FENCE_STATUS_NV), the status remains TRUE until the next SetFenceNV of the fence.
85            if (mStatus == GL_TRUE)
86            {
87                return GL_TRUE;
88            }
89
90            mStatus = (mFence->test(false) ? GL_TRUE : GL_FALSE);
91            return mStatus;
92        }
93
94      case GL_FENCE_CONDITION_NV:
95        return mCondition;
96
97      default: UNREACHABLE(); return 0;
98    }
99}
100
101FenceSync::FenceSync(rx::Renderer *renderer, GLuint id)
102    : RefCountObject(id)
103{
104    mFence = renderer->createFence();
105
106    LARGE_INTEGER counterFreqency = { 0 };
107    BOOL success = QueryPerformanceFrequency(&counterFreqency);
108    UNUSED_ASSERTION_VARIABLE(success);
109    ASSERT(success);
110
111    mCounterFrequency = counterFreqency.QuadPart;
112}
113
114FenceSync::~FenceSync()
115{
116    delete mFence;
117}
118
119void FenceSync::set(GLenum condition)
120{
121    mCondition = condition;
122    mFence->set();
123}
124
125GLenum FenceSync::clientWait(GLbitfield flags, GLuint64 timeout)
126{
127    ASSERT(mFence->isSet());
128
129    bool flushCommandBuffer = ((flags & GL_SYNC_FLUSH_COMMANDS_BIT) != 0);
130
131    if (mFence->test(flushCommandBuffer))
132    {
133        return GL_ALREADY_SIGNALED;
134    }
135
136    if (mFence->hasError())
137    {
138        return GL_WAIT_FAILED;
139    }
140
141    if (timeout == 0)
142    {
143        return GL_TIMEOUT_EXPIRED;
144    }
145
146    LARGE_INTEGER currentCounter = { 0 };
147    BOOL success = QueryPerformanceCounter(&currentCounter);
148    UNUSED_ASSERTION_VARIABLE(success);
149    ASSERT(success);
150
151    LONGLONG timeoutInSeconds = static_cast<LONGLONG>(timeout) * static_cast<LONGLONG>(1000000ll);
152    LONGLONG endCounter = currentCounter.QuadPart + mCounterFrequency * timeoutInSeconds;
153
154    while (currentCounter.QuadPart < endCounter && !mFence->test(flushCommandBuffer))
155    {
156        Sleep(0);
157        BOOL success = QueryPerformanceCounter(&currentCounter);
158        UNUSED_ASSERTION_VARIABLE(success);
159        ASSERT(success);
160    }
161
162    if (mFence->hasError())
163    {
164        return GL_WAIT_FAILED;
165    }
166
167    if (currentCounter.QuadPart >= endCounter)
168    {
169        return GL_TIMEOUT_EXPIRED;
170    }
171
172    return GL_CONDITION_SATISFIED;
173}
174
175void FenceSync::serverWait()
176{
177    // Because our API is currently designed to be called from a single thread, we don't need to do
178    // extra work for a server-side fence. GPU commands issued after the fence is created will always
179    // be processed after the fence is signaled.
180}
181
182GLenum FenceSync::getStatus() const
183{
184    if (mFence->test(false))
185    {
186        // The spec does not specify any way to report errors during the status test (e.g. device lost)
187        // so we report the fence is unblocked in case of error or signaled.
188        return GL_SIGNALED;
189    }
190
191    return GL_UNSIGNALED;
192}
193
194}
195