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