Split FenceImpl into FenceNVImpl and FenceSyncImpl, and refactor.
Move Windows-specific code out of Fence.cpp. Split FenceImpl
based on suggestions on previous review
https://chromium-review.googlesource.com/221805/ . Refactored
further based on code review feedback and added first unit tests.
BUG=angleproject:774
Change-Id: I630034e1788e48ddb7722016ca22da474e785798
Reviewed-on: https://chromium-review.googlesource.com/222954
Reviewed-by: Kenneth Russell <kbr@chromium.org>
Tested-by: Kenneth Russell <kbr@chromium.org>
diff --git a/src/libGLESv2/Context.cpp b/src/libGLESv2/Context.cpp
index eab2dad..752212a 100644
--- a/src/libGLESv2/Context.cpp
+++ b/src/libGLESv2/Context.cpp
@@ -287,7 +287,7 @@
{
GLuint handle = mFenceNVHandleAllocator.allocate();
- mFenceNVMap[handle] = new FenceNV(mRenderer);
+ mFenceNVMap[handle] = new FenceNV(mRenderer->createFenceNV());
return handle;
}
diff --git a/src/libGLESv2/Fence.cpp b/src/libGLESv2/Fence.cpp
index c0bef12..0aacb9d 100644
--- a/src/libGLESv2/Fence.cpp
+++ b/src/libGLESv2/Fence.cpp
@@ -4,18 +4,8 @@
// found in the LICENSE file.
//
-// Fence.cpp: Implements the gl::Fence class, which supports the GL_NV_fence extension.
-
-// Important note on accurate timers in Windows:
-//
-// QueryPerformanceCounter has a few major issues, including being 10x as expensive to call
-// as timeGetTime on laptops and "jumping" during certain hardware events.
-//
-// See the comments at the top of the Chromium source file "chromium/src/base/time/time_win.cc"
-// https://code.google.com/p/chromium/codesearch#chromium/src/base/time/time_win.cc
-//
-// We still opt to use QPC. In the present and moving forward, most newer systems will not suffer
-// from buggy implementations.
+// Fence.cpp: Implements the gl::FenceNV and gl::FenceSync classes, which support the GL_NV_fence
+// extension and GLES3 sync objects.
#include "libGLESv2/Fence.h"
#include "libGLESv2/renderer/FenceImpl.h"
@@ -27,8 +17,8 @@
namespace gl
{
-FenceNV::FenceNV(rx::Renderer *renderer)
- : mFence(renderer->createFence()),
+FenceNV::FenceNV(rx::FenceNVImpl *impl)
+ : mFence(impl),
mIsSet(false),
mStatus(GL_FALSE),
mCondition(GL_NONE)
@@ -79,65 +69,14 @@
{
ASSERT(mIsSet);
- while (mStatus != GL_TRUE)
- {
- Error error = mFence->test(true, &mStatus);
- if (error.isError())
- {
- return error;
- }
-
- Sleep(0);
- }
-
- return Error(GL_NO_ERROR);
+ return mFence->finishFence(&mStatus);
}
-Error FenceNV::getFencei(GLenum pname, GLint *params)
-{
- ASSERT(mIsSet);
-
- switch (pname)
- {
- case GL_FENCE_STATUS_NV:
- // GL_NV_fence spec:
- // Once the status of a fence has been finished (via FinishFenceNV) or tested and the returned status is TRUE (via either TestFenceNV
- // or GetFenceivNV querying the FENCE_STATUS_NV), the status remains TRUE until the next SetFenceNV of the fence.
- if (mStatus != GL_TRUE)
- {
- Error error = mFence->test(false, &mStatus);
- if (error.isError())
- {
- return error;
- }
- }
- *params = mStatus;
- break;
-
- case GL_FENCE_CONDITION_NV:
- *params = mCondition;
- break;
-
- default:
- UNREACHABLE();
- return gl::Error(GL_INVALID_OPERATION);
- }
-
- return Error(GL_NO_ERROR);
-}
-
-FenceSync::FenceSync(rx::Renderer *renderer, GLuint id)
+FenceSync::FenceSync(rx::FenceSyncImpl *impl, GLuint id)
: RefCountObject(id),
- mFence(renderer->createFence()),
- mCounterFrequency(0),
+ mFence(impl),
mCondition(GL_NONE)
{
- LARGE_INTEGER counterFreqency = { 0 };
- BOOL success = QueryPerformanceFrequency(&counterFreqency);
- UNUSED_ASSERTION_VARIABLE(success);
- ASSERT(success);
-
- mCounterFrequency = counterFreqency.QuadPart;
}
FenceSync::~FenceSync()
@@ -160,87 +99,17 @@
Error FenceSync::clientWait(GLbitfield flags, GLuint64 timeout, GLenum *outResult)
{
ASSERT(mCondition != GL_NONE);
-
- bool flushCommandBuffer = ((flags & GL_SYNC_FLUSH_COMMANDS_BIT) != 0);
-
- GLboolean result = GL_FALSE;
- Error error = mFence->test(flushCommandBuffer, &result);
- if (error.isError())
- {
- *outResult = GL_WAIT_FAILED;
- return error;
- }
-
- if (result == GL_TRUE)
- {
- *outResult = GL_ALREADY_SIGNALED;
- return Error(GL_NO_ERROR);
- }
-
- if (timeout == 0)
- {
- *outResult = GL_TIMEOUT_EXPIRED;
- return Error(GL_NO_ERROR);
- }
-
- LARGE_INTEGER currentCounter = { 0 };
- BOOL success = QueryPerformanceCounter(¤tCounter);
- UNUSED_ASSERTION_VARIABLE(success);
- ASSERT(success);
-
- LONGLONG timeoutInSeconds = static_cast<LONGLONG>(timeout) * static_cast<LONGLONG>(1000000ll);
- LONGLONG endCounter = currentCounter.QuadPart + mCounterFrequency * timeoutInSeconds;
-
- while (currentCounter.QuadPart < endCounter && !result)
- {
- Sleep(0);
- BOOL success = QueryPerformanceCounter(¤tCounter);
- UNUSED_ASSERTION_VARIABLE(success);
- ASSERT(success);
-
- error = mFence->test(flushCommandBuffer, &result);
- if (error.isError())
- {
- *outResult = GL_WAIT_FAILED;
- return error;
- }
- }
-
- if (currentCounter.QuadPart >= endCounter)
- {
- *outResult = GL_TIMEOUT_EXPIRED;
- }
- else
- {
- *outResult = GL_CONDITION_SATISFIED;
- }
-
- return Error(GL_NO_ERROR);
+ return mFence->clientWait(flags, timeout, outResult);
}
-Error FenceSync::serverWait()
+Error FenceSync::serverWait(GLbitfield flags, GLuint64 timeout)
{
- // Because our API is currently designed to be called from a single thread, we don't need to do
- // extra work for a server-side fence. GPU commands issued after the fence is created will always
- // be processed after the fence is signaled.
- return Error(GL_NO_ERROR);
+ return mFence->serverWait(flags, timeout);
}
Error FenceSync::getStatus(GLint *outResult) const
{
- GLboolean result = GL_FALSE;
- Error error = mFence->test(false, &result);
- if (error.isError())
- {
- // The spec does not specify any way to report errors during the status test (e.g. device lost)
- // so we report the fence is unblocked in case of error or signaled.
- *outResult = GL_SIGNALED;
-
- return error;
- }
-
- *outResult = (result ? GL_SIGNALED : GL_UNSIGNALED);
- return Error(GL_NO_ERROR);
+ return mFence->getStatus(outResult);
}
}
diff --git a/src/libGLESv2/Fence.h b/src/libGLESv2/Fence.h
index 0c4919d..411fa2c 100644
--- a/src/libGLESv2/Fence.h
+++ b/src/libGLESv2/Fence.h
@@ -4,7 +4,8 @@
// found in the LICENSE file.
//
-// Fence.h: Defines the gl::Fence class, which supports the GL_NV_fence extension.
+// Fence.h: Defines the gl::FenceNV and gl::FenceSync classes, which support the GL_NV_fence
+// extension and GLES3 sync objects.
#ifndef LIBGLESV2_FENCE_H_
#define LIBGLESV2_FENCE_H_
@@ -17,7 +18,8 @@
namespace rx
{
class Renderer;
-class FenceImpl;
+class FenceNVImpl;
+class FenceSyncImpl;
}
namespace gl
@@ -26,14 +28,13 @@
class FenceNV
{
public:
- explicit FenceNV(rx::Renderer *renderer);
+ explicit FenceNV(rx::FenceNVImpl *impl);
virtual ~FenceNV();
GLboolean isFence() const;
Error setFence(GLenum condition);
Error testFence(GLboolean *outResult);
Error finishFence();
- Error getFencei(GLenum pname, GLint *params);
GLboolean getStatus() const { return mStatus; }
GLuint getCondition() const { return mCondition; }
@@ -41,7 +42,7 @@
private:
DISALLOW_COPY_AND_ASSIGN(FenceNV);
- rx::FenceImpl *mFence;
+ rx::FenceNVImpl *mFence;
bool mIsSet;
@@ -52,12 +53,12 @@
class FenceSync : public RefCountObject
{
public:
- explicit FenceSync(rx::Renderer *renderer, GLuint id);
+ explicit FenceSync(rx::FenceSyncImpl *impl, GLuint id);
virtual ~FenceSync();
Error set(GLenum condition);
Error clientWait(GLbitfield flags, GLuint64 timeout, GLenum *outResult);
- Error serverWait();
+ Error serverWait(GLbitfield flags, GLuint64 timeout);
Error getStatus(GLint *outResult) const;
GLuint getCondition() const { return mCondition; }
@@ -65,8 +66,7 @@
private:
DISALLOW_COPY_AND_ASSIGN(FenceSync);
- rx::FenceImpl *mFence;
- LONGLONG mCounterFrequency;
+ rx::FenceSyncImpl *mFence;
GLenum mCondition;
};
diff --git a/src/libGLESv2/ResourceManager.cpp b/src/libGLESv2/ResourceManager.cpp
index 9121de1..04fa841 100644
--- a/src/libGLESv2/ResourceManager.cpp
+++ b/src/libGLESv2/ResourceManager.cpp
@@ -146,7 +146,7 @@
{
GLuint handle = mFenceSyncHandleAllocator.allocate();
- FenceSync *fenceSync = new FenceSync(mRenderer, handle);
+ FenceSync *fenceSync = new FenceSync(mRenderer->createFenceSync(), handle);
fenceSync->addRef();
mFenceSyncMap[handle] = fenceSync;
diff --git a/src/libGLESv2/libGLESv2.cpp b/src/libGLESv2/libGLESv2.cpp
index f528222..054e310 100644
--- a/src/libGLESv2/libGLESv2.cpp
+++ b/src/libGLESv2/libGLESv2.cpp
@@ -2139,19 +2139,35 @@
switch (pname)
{
case GL_FENCE_STATUS_NV:
+ {
+ // GL_NV_fence spec:
+ // Once the status of a fence has been finished (via FinishFenceNV) or tested and the returned status is TRUE (via either TestFenceNV
+ // or GetFenceivNV querying the FENCE_STATUS_NV), the status remains TRUE until the next SetFenceNV of the fence.
+ GLboolean status = GL_TRUE;
+ if (fenceObject->getStatus() != GL_TRUE)
+ {
+ gl::Error error = fenceObject->testFence(&status);
+ if (error.isError())
+ {
+ context->recordError(error);
+ return;
+ }
+ }
+ *params = status;
+ break;
+ }
+
case GL_FENCE_CONDITION_NV:
- break;
+ {
+ *params = fenceObject->getCondition();
+ break;
+ }
default:
- context->recordError(gl::Error(GL_INVALID_ENUM));
- return;
- }
-
- gl::Error error = fenceObject->getFencei(pname, params);
- if (error.isError())
- {
- context->recordError(error);
- return;
+ {
+ context->recordError(gl::Error(GL_INVALID_ENUM));
+ return;
+ }
}
}
}
@@ -7548,7 +7564,7 @@
return;
}
- gl::Error error = fenceSync->serverWait();
+ gl::Error error = fenceSync->serverWait(flags, timeout);
if (error.isError())
{
context->recordError(error);
diff --git a/src/libGLESv2/renderer/FenceImpl.h b/src/libGLESv2/renderer/FenceImpl.h
index 8d56851..1dd4678 100644
--- a/src/libGLESv2/renderer/FenceImpl.h
+++ b/src/libGLESv2/renderer/FenceImpl.h
@@ -4,7 +4,7 @@
// found in the LICENSE file.
//
-// FenceImpl.h: Defines the rx::FenceImpl class.
+// FenceImpl.h: Defines the rx::FenceNVImpl and rx::FenceSyncImpl classes.
#ifndef LIBGLESV2_RENDERER_FENCEIMPL_H_
#define LIBGLESV2_RENDERER_FENCEIMPL_H_
@@ -13,20 +13,38 @@
#include "common/angleutils.h"
+#include "angle_gl.h"
+
namespace rx
{
-class FenceImpl
+class FenceNVImpl
{
public:
- FenceImpl() { };
- virtual ~FenceImpl() { };
+ FenceNVImpl() { };
+ virtual ~FenceNVImpl() { };
virtual gl::Error set() = 0;
virtual gl::Error test(bool flushCommandBuffer, GLboolean *outFinished) = 0;
+ virtual gl::Error finishFence(GLboolean *outFinished) = 0;
private:
- DISALLOW_COPY_AND_ASSIGN(FenceImpl);
+ DISALLOW_COPY_AND_ASSIGN(FenceNVImpl);
+};
+
+class FenceSyncImpl
+{
+ public:
+ FenceSyncImpl() { };
+ virtual ~FenceSyncImpl() { };
+
+ virtual gl::Error set() = 0;
+ virtual gl::Error clientWait(GLbitfield flags, GLuint64 timeout, GLenum *outResult) = 0;
+ virtual gl::Error serverWait(GLbitfield flags, GLuint64 timeout) = 0;
+ virtual gl::Error getStatus(GLint *outResult) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FenceSyncImpl);
};
}
diff --git a/src/libGLESv2/renderer/Renderer.h b/src/libGLESv2/renderer/Renderer.h
index 43c2dac..1730725 100644
--- a/src/libGLESv2/renderer/Renderer.h
+++ b/src/libGLESv2/renderer/Renderer.h
@@ -50,7 +50,8 @@
class VertexBuffer;
class IndexBuffer;
class QueryImpl;
-class FenceImpl;
+class FenceNVImpl;
+class FenceSyncImpl;
class BufferImpl;
class VertexArrayImpl;
class BufferStorage;
@@ -227,7 +228,8 @@
// Query and Fence creation
virtual QueryImpl *createQuery(GLenum type) = 0;
- virtual FenceImpl *createFence() = 0;
+ virtual FenceNVImpl *createFenceNV() = 0;
+ virtual FenceSyncImpl *createFenceSync() = 0;
// Transform Feedback creation
virtual TransformFeedbackImpl* createTransformFeedback() = 0;
diff --git a/src/libGLESv2/renderer/d3d/d3d11/Fence11.cpp b/src/libGLESv2/renderer/d3d/d3d11/Fence11.cpp
index c0f462a..e96c515 100644
--- a/src/libGLESv2/renderer/d3d/d3d11/Fence11.cpp
+++ b/src/libGLESv2/renderer/d3d/d3d11/Fence11.cpp
@@ -4,7 +4,7 @@
// found in the LICENSE file.
//
-// Fence11.cpp: Defines the rx::Fence11 class which implements rx::FenceImpl.
+// Fence11.cpp: Defines the rx::FenceNV11 and rx::FenceSync11 classes which implement rx::FenceNVImpl and rx::FenceSyncImpl.
#include "libGLESv2/renderer/d3d/d3d11/Fence11.h"
#include "libGLESv2/renderer/d3d/d3d11/Renderer11.h"
@@ -13,48 +13,43 @@
namespace rx
{
-Fence11::Fence11(rx::Renderer11 *renderer)
- : mRenderer(renderer),
- mQuery(NULL)
-{
-}
+//
+// Template helpers for set and test operations.
+//
-Fence11::~Fence11()
+template<class FenceClass>
+gl::Error FenceSetHelper(FenceClass *fence)
{
- SafeRelease(mQuery);
-}
-
-gl::Error Fence11::set()
-{
- if (!mQuery)
+ if (!fence->mQuery)
{
D3D11_QUERY_DESC queryDesc;
queryDesc.Query = D3D11_QUERY_EVENT;
queryDesc.MiscFlags = 0;
- HRESULT result = mRenderer->getDevice()->CreateQuery(&queryDesc, &mQuery);
+ HRESULT result = fence->mRenderer->getDevice()->CreateQuery(&queryDesc, &fence->mQuery);
if (FAILED(result))
{
return gl::Error(GL_OUT_OF_MEMORY, "Failed to create event query, result: 0x%X.", result);
}
}
- mRenderer->getDeviceContext()->End(mQuery);
+ fence->mRenderer->getDeviceContext()->End(fence->mQuery);
return gl::Error(GL_NO_ERROR);
}
-gl::Error Fence11::test(bool flushCommandBuffer, GLboolean *outFinished)
+template <class FenceClass>
+gl::Error FenceTestHelper(FenceClass *fence, bool flushCommandBuffer, GLboolean *outFinished)
{
- ASSERT(mQuery);
+ ASSERT(fence->mQuery);
UINT getDataFlags = (flushCommandBuffer ? 0 : D3D11_ASYNC_GETDATA_DONOTFLUSH);
- HRESULT result = mRenderer->getDeviceContext()->GetData(mQuery, NULL, 0, getDataFlags);
+ HRESULT result = fence->mRenderer->getDeviceContext()->GetData(fence->mQuery, NULL, 0, getDataFlags);
if (FAILED(result))
{
return gl::Error(GL_OUT_OF_MEMORY, "Failed to get query data, result: 0x%X.", result);
}
- else if (mRenderer->isDeviceLost())
+ else if (fence->mRenderer->isDeviceLost())
{
return gl::Error(GL_OUT_OF_MEMORY, "Device was lost while querying result of an event query.");
}
@@ -64,4 +59,172 @@
return gl::Error(GL_NO_ERROR);
}
+//
+// FenceNV11
+//
+
+FenceNV11::FenceNV11(Renderer11 *renderer)
+ : FenceNVImpl(),
+ mRenderer(renderer),
+ mQuery(NULL)
+{
}
+
+FenceNV11::~FenceNV11()
+{
+ SafeRelease(mQuery);
+}
+
+gl::Error FenceNV11::set()
+{
+ return FenceSetHelper(this);
+}
+
+gl::Error FenceNV11::test(bool flushCommandBuffer, GLboolean *outFinished)
+{
+ return FenceTestHelper(this, flushCommandBuffer, outFinished);
+}
+
+gl::Error FenceNV11::finishFence(GLboolean *outFinished)
+{
+ ASSERT(outFinished);
+
+ while (*outFinished != GL_TRUE)
+ {
+ gl::Error error = test(true, outFinished);
+ if (error.isError())
+ {
+ return error;
+ }
+
+ Sleep(0);
+ }
+
+ return gl::Error(GL_NO_ERROR);
+}
+
+//
+// FenceSync11
+//
+
+// Important note on accurate timers in Windows:
+//
+// QueryPerformanceCounter has a few major issues, including being 10x as expensive to call
+// as timeGetTime on laptops and "jumping" during certain hardware events.
+//
+// See the comments at the top of the Chromium source file "chromium/src/base/time/time_win.cc"
+// https://code.google.com/p/chromium/codesearch#chromium/src/base/time/time_win.cc
+//
+// We still opt to use QPC. In the present and moving forward, most newer systems will not suffer
+// from buggy implementations.
+
+FenceSync11::FenceSync11(Renderer11 *renderer)
+ : FenceSyncImpl(),
+ mRenderer(renderer),
+ mQuery(NULL)
+{
+ LARGE_INTEGER counterFreqency = { 0 };
+ BOOL success = QueryPerformanceFrequency(&counterFreqency);
+ UNUSED_ASSERTION_VARIABLE(success);
+ ASSERT(success);
+
+ mCounterFrequency = counterFreqency.QuadPart;
+}
+
+FenceSync11::~FenceSync11()
+{
+ SafeRelease(mQuery);
+}
+
+gl::Error FenceSync11::set()
+{
+ return FenceSetHelper(this);
+}
+
+gl::Error FenceSync11::clientWait(GLbitfield flags, GLuint64 timeout, GLenum *outResult)
+{
+ ASSERT(outResult);
+
+ bool flushCommandBuffer = ((flags & GL_SYNC_FLUSH_COMMANDS_BIT) != 0);
+
+ GLboolean result = GL_FALSE;
+ gl::Error error = FenceTestHelper(this, flushCommandBuffer, &result);
+ if (error.isError())
+ {
+ *outResult = GL_WAIT_FAILED;
+ return error;
+ }
+
+ if (result == GL_TRUE)
+ {
+ *outResult = GL_ALREADY_SIGNALED;
+ return gl::Error(GL_NO_ERROR);
+ }
+
+ if (timeout == 0)
+ {
+ *outResult = GL_TIMEOUT_EXPIRED;
+ return gl::Error(GL_NO_ERROR);
+ }
+
+ LARGE_INTEGER currentCounter = { 0 };
+ BOOL success = QueryPerformanceCounter(¤tCounter);
+ UNUSED_ASSERTION_VARIABLE(success);
+ ASSERT(success);
+
+ LONGLONG timeoutInSeconds = static_cast<LONGLONG>(timeout) * static_cast<LONGLONG>(1000000ll);
+ LONGLONG endCounter = currentCounter.QuadPart + mCounterFrequency * timeoutInSeconds;
+
+ while (currentCounter.QuadPart < endCounter && !result)
+ {
+ Sleep(0);
+ BOOL success = QueryPerformanceCounter(¤tCounter);
+ UNUSED_ASSERTION_VARIABLE(success);
+ ASSERT(success);
+
+ error = FenceTestHelper(this, flushCommandBuffer, &result);
+ if (error.isError())
+ {
+ *outResult = GL_WAIT_FAILED;
+ return error;
+ }
+ }
+
+ if (currentCounter.QuadPart >= endCounter)
+ {
+ *outResult = GL_TIMEOUT_EXPIRED;
+ }
+ else
+ {
+ *outResult = GL_CONDITION_SATISFIED;
+ }
+
+ return gl::Error(GL_NO_ERROR);
+}
+
+gl::Error FenceSync11::serverWait(GLbitfield flags, GLuint64 timeout)
+{
+ // Because our API is currently designed to be called from a single thread, we don't need to do
+ // extra work for a server-side fence. GPU commands issued after the fence is created will always
+ // be processed after the fence is signaled.
+ return gl::Error(GL_NO_ERROR);
+}
+
+gl::Error FenceSync11::getStatus(GLint *outResult)
+{
+ GLboolean result = GL_FALSE;
+ gl::Error error = FenceTestHelper(this, false, &result);
+ if (error.isError())
+ {
+ // The spec does not specify any way to report errors during the status test (e.g. device lost)
+ // so we report the fence is unblocked in case of error or signaled.
+ *outResult = GL_SIGNALED;
+
+ return error;
+ }
+
+ *outResult = (result ? GL_SIGNALED : GL_UNSIGNALED);
+ return gl::Error(GL_NO_ERROR);
+}
+
+} // namespace rx
diff --git a/src/libGLESv2/renderer/d3d/d3d11/Fence11.h b/src/libGLESv2/renderer/d3d/d3d11/Fence11.h
index 9347197..1223a53 100644
--- a/src/libGLESv2/renderer/d3d/d3d11/Fence11.h
+++ b/src/libGLESv2/renderer/d3d/d3d11/Fence11.h
@@ -4,10 +4,10 @@
// found in the LICENSE file.
//
-// Fence11.h: Defines the rx::Fence11 class which implements rx::FenceImpl.
+// Fence11.h: Defines the rx::FenceNV11 and rx::FenceSync11 classes which implement rx::FenceNVImpl and rx::FenceSyncImpl.
-#ifndef LIBGLESV2_RENDERER_Fence11_H_
-#define LIBGLESV2_RENDERER_Fence11_H_
+#ifndef LIBGLESV2_RENDERER_FENCE11_H_
+#define LIBGLESV2_RENDERER_FENCE11_H_
#include "libGLESv2/renderer/FenceImpl.h"
@@ -15,22 +15,48 @@
{
class Renderer11;
-class Fence11 : public FenceImpl
+class FenceNV11 : public FenceNVImpl
{
public:
- explicit Fence11(rx::Renderer11 *renderer);
- virtual ~Fence11();
+ explicit FenceNV11(Renderer11 *renderer);
+ virtual ~FenceNV11();
gl::Error set();
gl::Error test(bool flushCommandBuffer, GLboolean *outFinished);
+ gl::Error finishFence(GLboolean *outFinished);
private:
- DISALLOW_COPY_AND_ASSIGN(Fence11);
+ DISALLOW_COPY_AND_ASSIGN(FenceNV11);
- rx::Renderer11 *mRenderer;
+ template<class T> friend gl::Error FenceSetHelper(T *fence);
+ template<class T> friend gl::Error FenceTestHelper(T *fence, bool flushCommandBuffer, GLboolean *outFinished);
+
+ Renderer11 *mRenderer;
ID3D11Query *mQuery;
};
+class FenceSync11 : public FenceSyncImpl
+{
+ public:
+ explicit FenceSync11(Renderer11 *renderer);
+ virtual ~FenceSync11();
+
+ gl::Error set();
+ gl::Error clientWait(GLbitfield flags, GLuint64 timeout, GLenum *outResult);
+ gl::Error serverWait(GLbitfield flags, GLuint64 timeout);
+ gl::Error getStatus(GLint *outResult);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FenceSync11);
+
+ template<class T> friend gl::Error FenceSetHelper(T *fence);
+ template<class T> friend gl::Error FenceTestHelper(T *fence, bool flushCommandBuffer, GLboolean *outFinished);
+
+ Renderer11 *mRenderer;
+ ID3D11Query *mQuery;
+ LONGLONG mCounterFrequency;
+};
+
}
#endif // LIBGLESV2_RENDERER_FENCE11_H_
diff --git a/src/libGLESv2/renderer/d3d/d3d11/Renderer11.cpp b/src/libGLESv2/renderer/d3d/d3d11/Renderer11.cpp
index e58076d..3f92cd4 100644
--- a/src/libGLESv2/renderer/d3d/d3d11/Renderer11.cpp
+++ b/src/libGLESv2/renderer/d3d/d3d11/Renderer11.cpp
@@ -2478,9 +2478,14 @@
return new Query11(this, type);
}
-FenceImpl *Renderer11::createFence()
+FenceNVImpl *Renderer11::createFenceNV()
{
- return new Fence11(this);
+ return new FenceNV11(this);
+}
+
+FenceSyncImpl *Renderer11::createFenceSync()
+{
+ return new FenceSync11(this);
}
TransformFeedbackImpl* Renderer11::createTransformFeedback()
diff --git a/src/libGLESv2/renderer/d3d/d3d11/Renderer11.h b/src/libGLESv2/renderer/d3d/d3d11/Renderer11.h
index b36c316..86935bc 100644
--- a/src/libGLESv2/renderer/d3d/d3d11/Renderer11.h
+++ b/src/libGLESv2/renderer/d3d/d3d11/Renderer11.h
@@ -172,7 +172,8 @@
// Query and Fence creation
virtual QueryImpl *createQuery(GLenum type);
- virtual FenceImpl *createFence();
+ virtual FenceNVImpl *createFenceNV();
+ virtual FenceSyncImpl *createFenceSync();
// Transform Feedback creation
virtual TransformFeedbackImpl* createTransformFeedback();
diff --git a/src/libGLESv2/renderer/d3d/d3d9/Fence9.cpp b/src/libGLESv2/renderer/d3d/d3d9/Fence9.cpp
index 5140e96..66263fe 100644
--- a/src/libGLESv2/renderer/d3d/d3d9/Fence9.cpp
+++ b/src/libGLESv2/renderer/d3d/d3d9/Fence9.cpp
@@ -4,7 +4,7 @@
// found in the LICENSE file.
//
-// Fence9.cpp: Defines the rx::Fence9 class.
+// Fence9.cpp: Defines the rx::FenceNV9 class.
#include "libGLESv2/renderer/d3d/d3d9/Fence9.h"
#include "libGLESv2/renderer/d3d/d3d9/renderer9_utils.h"
@@ -14,18 +14,19 @@
namespace rx
{
-Fence9::Fence9(rx::Renderer9 *renderer)
- : mRenderer(renderer),
+FenceNV9::FenceNV9(Renderer9 *renderer)
+ : FenceNVImpl(),
+ mRenderer(renderer),
mQuery(NULL)
{
}
-Fence9::~Fence9()
+FenceNV9::~FenceNV9()
{
SafeRelease(mQuery);
}
-gl::Error Fence9::set()
+gl::Error FenceNV9::set()
{
if (!mQuery)
{
@@ -47,7 +48,7 @@
return gl::Error(GL_NO_ERROR);
}
-gl::Error Fence9::test(bool flushCommandBuffer, GLboolean *outFinished)
+gl::Error FenceNV9::test(bool flushCommandBuffer, GLboolean *outFinished)
{
ASSERT(mQuery);
@@ -69,4 +70,22 @@
return gl::Error(GL_NO_ERROR);
}
+gl::Error FenceNV9::finishFence(GLboolean *outFinished)
+{
+ ASSERT(outFinished);
+
+ while (*outFinished != GL_TRUE)
+ {
+ gl::Error error = test(true, outFinished);
+ if (error.isError())
+ {
+ return error;
+ }
+
+ Sleep(0);
+ }
+
+ return gl::Error(GL_NO_ERROR);
+}
+
}
diff --git a/src/libGLESv2/renderer/d3d/d3d9/Fence9.h b/src/libGLESv2/renderer/d3d/d3d9/Fence9.h
index 3bf9d08..d7873d5 100644
--- a/src/libGLESv2/renderer/d3d/d3d9/Fence9.h
+++ b/src/libGLESv2/renderer/d3d/d3d9/Fence9.h
@@ -4,7 +4,7 @@
// found in the LICENSE file.
//
-// Fence9.h: Defines the rx::Fence9 class which implements rx::FenceImpl.
+// Fence9.h: Defines the rx::FenceNV9 class which implements rx::FenceNVImpl.
#ifndef LIBGLESV2_RENDERER_FENCE9_H_
#define LIBGLESV2_RENDERER_FENCE9_H_
@@ -15,19 +15,20 @@
{
class Renderer9;
-class Fence9 : public FenceImpl
+class FenceNV9 : public FenceNVImpl
{
public:
- explicit Fence9(rx::Renderer9 *renderer);
- virtual ~Fence9();
+ explicit FenceNV9(Renderer9 *renderer);
+ virtual ~FenceNV9();
gl::Error set();
gl::Error test(bool flushCommandBuffer, GLboolean *outFinished);
+ gl::Error finishFence(GLboolean *outFinished);
private:
- DISALLOW_COPY_AND_ASSIGN(Fence9);
+ DISALLOW_COPY_AND_ASSIGN(FenceNV9);
- rx::Renderer9 *mRenderer;
+ Renderer9 *mRenderer;
IDirect3DQuery9 *mQuery;
};
diff --git a/src/libGLESv2/renderer/d3d/d3d9/Renderer9.cpp b/src/libGLESv2/renderer/d3d/d3d9/Renderer9.cpp
index 6e2bc6d..e0afaa2 100644
--- a/src/libGLESv2/renderer/d3d/d3d9/Renderer9.cpp
+++ b/src/libGLESv2/renderer/d3d/d3d9/Renderer9.cpp
@@ -637,9 +637,16 @@
return new Query9(this, type);
}
-FenceImpl *Renderer9::createFence()
+FenceNVImpl *Renderer9::createFenceNV()
{
- return new Fence9(this);
+ return new FenceNV9(this);
+}
+
+FenceSyncImpl *Renderer9::createFenceSync()
+{
+ // Renderer9 doesn't support ES 3.0 and its sync objects.
+ UNREACHABLE();
+ return NULL;
}
TransformFeedbackImpl* Renderer9::createTransformFeedback()
diff --git a/src/libGLESv2/renderer/d3d/d3d9/Renderer9.h b/src/libGLESv2/renderer/d3d/d3d9/Renderer9.h
index 7d4c73c..3bd8902 100644
--- a/src/libGLESv2/renderer/d3d/d3d9/Renderer9.h
+++ b/src/libGLESv2/renderer/d3d/d3d9/Renderer9.h
@@ -174,7 +174,8 @@
// Query and Fence creation
virtual QueryImpl *createQuery(GLenum type);
- virtual FenceImpl *createFence();
+ virtual FenceNVImpl *createFenceNV();
+ virtual FenceSyncImpl *createFenceSync();
// Transform Feedback creation
virtual TransformFeedbackImpl* createTransformFeedback();
diff --git a/tests/angle_implementation_unit_tests/Fence_unittest.cpp b/tests/angle_implementation_unit_tests/Fence_unittest.cpp
new file mode 100644
index 0000000..27596a0
--- /dev/null
+++ b/tests/angle_implementation_unit_tests/Fence_unittest.cpp
@@ -0,0 +1,162 @@
+//
+// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "libGLESv2/Fence.h"
+#include "libGLESv2/renderer/FenceImpl.h"
+
+using ::testing::_;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+
+namespace {
+
+//
+// FenceNV tests
+//
+
+class MockFenceNVImpl : public rx::FenceNVImpl
+{
+ public:
+ virtual ~MockFenceNVImpl() { destroy(); }
+
+ MOCK_METHOD0(set, gl::Error());
+ MOCK_METHOD2(test, gl::Error(bool, GLboolean *));
+ MOCK_METHOD1(finishFence, gl::Error(GLboolean *));
+
+ MOCK_METHOD0(destroy, void());
+};
+
+class FenceNVTest : public testing::Test
+{
+ protected:
+ virtual void SetUp()
+ {
+ mImpl = new MockFenceNVImpl;
+ EXPECT_CALL(*mImpl, destroy());
+ mFence = new gl::FenceNV(mImpl);
+ }
+
+ virtual void TearDown()
+ {
+ delete mFence;
+ }
+
+ MockFenceNVImpl *mImpl;
+ gl::FenceNV* mFence;
+};
+
+TEST_F(FenceNVTest, DestructionDeletesImpl)
+{
+ MockFenceNVImpl* impl = new MockFenceNVImpl;
+ EXPECT_CALL(*impl, destroy()).Times(1).RetiresOnSaturation();
+
+ gl::FenceNV* fence = new gl::FenceNV(impl);
+ delete fence;
+
+ // Only needed because the mock is leaked if bugs are present,
+ // which logs an error, but does not cause the test to fail.
+ // Ordinarily mocks are verified when destroyed.
+ testing::Mock::VerifyAndClear(impl);
+}
+
+TEST_F(FenceNVTest, SetAndTestBehavior)
+{
+ EXPECT_CALL(*mImpl, set())
+ .WillOnce(Return(gl::Error(GL_NO_ERROR)))
+ .RetiresOnSaturation();
+ EXPECT_EQ(GL_FALSE, mFence->isFence());
+ mFence->setFence(GL_ALL_COMPLETED_NV);
+ EXPECT_EQ(GL_TRUE, mFence->isFence());
+ // Fake the behavior of testing the fence before and after it's passed.
+ EXPECT_CALL(*mImpl, test(_, _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(GL_FALSE),
+ Return(gl::Error(GL_NO_ERROR))))
+ .WillOnce(DoAll(SetArgumentPointee<1>(GL_TRUE),
+ Return(gl::Error(GL_NO_ERROR))))
+ .RetiresOnSaturation();
+ GLboolean out;
+ mFence->testFence(&out);
+ EXPECT_EQ(GL_FALSE, out);
+ mFence->testFence(&out);
+ EXPECT_EQ(GL_TRUE, out);
+}
+
+//
+// FenceSync tests
+//
+
+class MockFenceSyncImpl : public rx::FenceSyncImpl
+{
+ public:
+ virtual ~MockFenceSyncImpl() { destroy(); }
+
+ MOCK_METHOD0(set, gl::Error());
+ MOCK_METHOD3(clientWait, gl::Error(GLbitfield, GLuint64, GLenum *));
+ MOCK_METHOD2(serverWait, gl::Error(GLbitfield, GLuint64));
+ MOCK_METHOD1(getStatus, gl::Error(GLint *));
+
+ MOCK_METHOD0(destroy, void());
+};
+
+class FenceSyncTest : public testing::Test
+{
+ protected:
+ virtual void SetUp()
+ {
+ mImpl = new MockFenceSyncImpl;
+ EXPECT_CALL(*mImpl, destroy());
+ mFence = new gl::FenceSync(mImpl, 1);
+ mFence->addRef();
+ }
+
+ virtual void TearDown()
+ {
+ mFence->release();
+ }
+
+ MockFenceSyncImpl *mImpl;
+ gl::FenceSync* mFence;
+};
+
+TEST_F(FenceSyncTest, DestructionDeletesImpl)
+{
+ MockFenceSyncImpl* impl = new MockFenceSyncImpl;
+ EXPECT_CALL(*impl, destroy()).Times(1).RetiresOnSaturation();
+
+ gl::FenceSync* fence = new gl::FenceSync(impl, 1);
+ fence->addRef();
+ fence->release();
+
+ // Only needed because the mock is leaked if bugs are present,
+ // which logs an error, but does not cause the test to fail.
+ // Ordinarily mocks are verified when destroyed.
+ testing::Mock::VerifyAndClear(impl);
+}
+
+TEST_F(FenceSyncTest, SetAndGetStatusBehavior)
+{
+ EXPECT_CALL(*mImpl, set())
+ .WillOnce(Return(gl::Error(GL_NO_ERROR)))
+ .RetiresOnSaturation();
+ mFence->set(GL_SYNC_GPU_COMMANDS_COMPLETE);
+ EXPECT_EQ(GL_SYNC_GPU_COMMANDS_COMPLETE, mFence->getCondition());
+ // Fake the behavior of testing the fence before and after it's passed.
+ EXPECT_CALL(*mImpl, getStatus(_))
+ .WillOnce(DoAll(SetArgumentPointee<0>(GL_UNSIGNALED),
+ Return(gl::Error(GL_NO_ERROR))))
+ .WillOnce(DoAll(SetArgumentPointee<0>(GL_SIGNALED),
+ Return(gl::Error(GL_NO_ERROR))))
+ .RetiresOnSaturation();
+ GLint out;
+ mFence->getStatus(&out);
+ EXPECT_EQ(GL_UNSIGNALED, out);
+ mFence->getStatus(&out);
+ EXPECT_EQ(GL_SIGNALED, out);
+}
+
+} // namespace