• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&currentCounter);
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(&currentCounter);
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