1 //
2 // Copyright (c) 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 // Fence11.cpp: Defines the rx::FenceNV11 and rx::Sync11 classes which implement
8 // rx::FenceNVImpl and rx::SyncImpl.
9 
10 #include "libANGLE/renderer/d3d/d3d11/Fence11.h"
11 #include "libANGLE/renderer/d3d/d3d11/Renderer11.h"
12 
13 #include "common/utilities.h"
14 
15 namespace rx
16 {
17 
18 static const int kDeviceLostCheckPeriod = 64;
19 
20 //
21 // Template helpers for set and test operations.
22 //
23 
24 template <class FenceClass>
FenceSetHelper(FenceClass * fence)25 gl::Error FenceSetHelper(FenceClass *fence)
26 {
27     if (!fence->mQuery)
28     {
29         D3D11_QUERY_DESC queryDesc;
30         queryDesc.Query     = D3D11_QUERY_EVENT;
31         queryDesc.MiscFlags = 0;
32 
33         HRESULT result = fence->mRenderer->getDevice()->CreateQuery(&queryDesc, &fence->mQuery);
34         if (FAILED(result))
35         {
36             return gl::OutOfMemory() << "Failed to create event query, " << gl::FmtHR(result);
37         }
38     }
39 
40     fence->mRenderer->getDeviceContext()->End(fence->mQuery);
41     return gl::NoError();
42 }
43 
44 template <class FenceClass>
FenceTestHelper(FenceClass * fence,bool flushCommandBuffer,GLboolean * outFinished)45 gl::Error FenceTestHelper(FenceClass *fence, bool flushCommandBuffer, GLboolean *outFinished)
46 {
47     ASSERT(fence->mQuery);
48 
49     UINT getDataFlags = (flushCommandBuffer ? 0 : D3D11_ASYNC_GETDATA_DONOTFLUSH);
50     HRESULT result =
51         fence->mRenderer->getDeviceContext()->GetData(fence->mQuery, nullptr, 0, getDataFlags);
52 
53     if (FAILED(result))
54     {
55         return gl::OutOfMemory() << "Failed to get query data, " << gl::FmtHR(result);
56     }
57 
58     ASSERT(result == S_OK || result == S_FALSE);
59     *outFinished = ((result == S_OK) ? GL_TRUE : GL_FALSE);
60     return gl::NoError();
61 }
62 
63 //
64 // FenceNV11
65 //
66 
FenceNV11(Renderer11 * renderer)67 FenceNV11::FenceNV11(Renderer11 *renderer) : FenceNVImpl(), mRenderer(renderer), mQuery(nullptr)
68 {
69 }
70 
~FenceNV11()71 FenceNV11::~FenceNV11()
72 {
73     SafeRelease(mQuery);
74 }
75 
set(GLenum condition)76 gl::Error FenceNV11::set(GLenum condition)
77 {
78     return FenceSetHelper(this);
79 }
80 
test(GLboolean * outFinished)81 gl::Error FenceNV11::test(GLboolean *outFinished)
82 {
83     return FenceTestHelper(this, true, outFinished);
84 }
85 
finish()86 gl::Error FenceNV11::finish()
87 {
88     GLboolean finished = GL_FALSE;
89 
90     int loopCount = 0;
91     while (finished != GL_TRUE)
92     {
93         loopCount++;
94         ANGLE_TRY(FenceTestHelper(this, true, &finished));
95 
96         if (loopCount % kDeviceLostCheckPeriod == 0 && mRenderer->testDeviceLost())
97         {
98             return gl::OutOfMemory() << "Device was lost while querying result of an event query.";
99         }
100 
101         ScheduleYield();
102     }
103 
104     return gl::NoError();
105 }
106 
107 //
108 // Sync11
109 //
110 
111 // Important note on accurate timers in Windows:
112 //
113 // QueryPerformanceCounter has a few major issues, including being 10x as expensive to call
114 // as timeGetTime on laptops and "jumping" during certain hardware events.
115 //
116 // See the comments at the top of the Chromium source file "chromium/src/base/time/time_win.cc"
117 //   https://code.google.com/p/chromium/codesearch#chromium/src/base/time/time_win.cc
118 //
119 // We still opt to use QPC. In the present and moving forward, most newer systems will not suffer
120 // from buggy implementations.
121 
Sync11(Renderer11 * renderer)122 Sync11::Sync11(Renderer11 *renderer) : SyncImpl(), mRenderer(renderer), mQuery(nullptr)
123 {
124     LARGE_INTEGER counterFreqency = {};
125     BOOL success                  = QueryPerformanceFrequency(&counterFreqency);
126     ASSERT(success);
127 
128     mCounterFrequency = counterFreqency.QuadPart;
129 }
130 
~Sync11()131 Sync11::~Sync11()
132 {
133     SafeRelease(mQuery);
134 }
135 
set(GLenum condition,GLbitfield flags)136 gl::Error Sync11::set(GLenum condition, GLbitfield flags)
137 {
138     ASSERT(condition == GL_SYNC_GPU_COMMANDS_COMPLETE && flags == 0);
139     return FenceSetHelper(this);
140 }
141 
clientWait(GLbitfield flags,GLuint64 timeout,GLenum * outResult)142 gl::Error Sync11::clientWait(GLbitfield flags, GLuint64 timeout, GLenum *outResult)
143 {
144     ASSERT(outResult);
145 
146     bool flushCommandBuffer = ((flags & GL_SYNC_FLUSH_COMMANDS_BIT) != 0);
147 
148     GLboolean result = GL_FALSE;
149     gl::Error error  = FenceTestHelper(this, flushCommandBuffer, &result);
150     if (error.isError())
151     {
152         *outResult = GL_WAIT_FAILED;
153         return error;
154     }
155 
156     if (result == GL_TRUE)
157     {
158         *outResult = GL_ALREADY_SIGNALED;
159         return gl::NoError();
160     }
161 
162     if (timeout == 0)
163     {
164         *outResult = GL_TIMEOUT_EXPIRED;
165         return gl::NoError();
166     }
167 
168     LARGE_INTEGER currentCounter = {};
169     BOOL success                 = QueryPerformanceCounter(&currentCounter);
170     ASSERT(success);
171 
172     LONGLONG timeoutInSeconds = static_cast<LONGLONG>(timeout / 1000000000ull);
173     LONGLONG endCounter       = currentCounter.QuadPart + mCounterFrequency * timeoutInSeconds;
174 
175     // Extremely unlikely, but if mCounterFrequency is large enough, endCounter can wrap
176     if (endCounter < currentCounter.QuadPart)
177     {
178         endCounter = MAXLONGLONG;
179     }
180 
181     int loopCount = 0;
182     while (currentCounter.QuadPart < endCounter && !result)
183     {
184         loopCount++;
185         ScheduleYield();
186         success = QueryPerformanceCounter(&currentCounter);
187         ASSERT(success);
188 
189         error = FenceTestHelper(this, flushCommandBuffer, &result);
190         if (error.isError())
191         {
192             *outResult = GL_WAIT_FAILED;
193             return error;
194         }
195 
196         if ((loopCount % kDeviceLostCheckPeriod) == 0 && mRenderer->testDeviceLost())
197         {
198             *outResult = GL_WAIT_FAILED;
199             return gl::OutOfMemory() << "Device was lost while querying result of an event query.";
200         }
201     }
202 
203     if (currentCounter.QuadPart >= endCounter)
204     {
205         *outResult = GL_TIMEOUT_EXPIRED;
206     }
207     else
208     {
209         *outResult = GL_CONDITION_SATISFIED;
210     }
211 
212     return gl::NoError();
213 }
214 
serverWait(GLbitfield flags,GLuint64 timeout)215 gl::Error Sync11::serverWait(GLbitfield flags, GLuint64 timeout)
216 {
217     // Because our API is currently designed to be called from a single thread, we don't need to do
218     // extra work for a server-side fence. GPU commands issued after the fence is created will
219     // always be processed after the fence is signaled.
220     return gl::NoError();
221 }
222 
getStatus(GLint * outResult)223 gl::Error Sync11::getStatus(GLint *outResult)
224 {
225     GLboolean result = GL_FALSE;
226     gl::Error error  = FenceTestHelper(this, false, &result);
227     if (error.isError())
228     {
229         // The spec does not specify any way to report errors during the status test (e.g. device
230         // lost) so we report the fence is unblocked in case of error or signaled.
231         *outResult = GL_SIGNALED;
232 
233         return error;
234     }
235 
236     *outResult = (result ? GL_SIGNALED : GL_UNSIGNALED);
237     return gl::NoError();
238 }
239 
240 }  // namespace rx
241