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
27 namespace gl
28 {
29
FenceNV(rx::Renderer * renderer)30 FenceNV::FenceNV(rx::Renderer *renderer)
31 {
32 mFence = renderer->createFence();
33 }
34
~FenceNV()35 FenceNV::~FenceNV()
36 {
37 delete mFence;
38 }
39
isFence() const40 GLboolean 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
setFence(GLenum condition)47 void FenceNV::setFence(GLenum condition)
48 {
49 mFence->set();
50
51 mCondition = condition;
52 mStatus = GL_FALSE;
53 }
54
testFence()55 GLboolean 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
finishFence()64 void FenceNV::finishFence()
65 {
66 ASSERT(mFence->isSet());
67
68 while (!mFence->test(true))
69 {
70 Sleep(0);
71 }
72 }
73
getFencei(GLenum pname)74 GLint 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
FenceSync(rx::Renderer * renderer,GLuint id)101 FenceSync::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
~FenceSync()114 FenceSync::~FenceSync()
115 {
116 delete mFence;
117 }
118
set(GLenum condition)119 void FenceSync::set(GLenum condition)
120 {
121 mCondition = condition;
122 mFence->set();
123 }
124
clientWait(GLbitfield flags,GLuint64 timeout)125 GLenum 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(¤tCounter);
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(¤tCounter);
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
serverWait()175 void 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
getStatus() const182 GLenum 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