Fence11.cpp (7147B)
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> 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> 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 66 FenceNV11::FenceNV11(Renderer11 *renderer) : FenceNVImpl(), mRenderer(renderer), mQuery(nullptr) {} 67 68 FenceNV11::~FenceNV11() 69 { 70 SafeRelease(mQuery); 71 } 72 73 angle::Result FenceNV11::set(const gl::Context *context, GLenum condition) 74 { 75 return FenceSetHelper(context, this); 76 } 77 78 angle::Result FenceNV11::test(const gl::Context *context, GLboolean *outFinished) 79 { 80 return FenceTestHelper(context, this, true, outFinished); 81 } 82 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 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 130 Sync11::~Sync11() 131 { 132 SafeRelease(mQuery); 133 } 134 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 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 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 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