Commit caa549c0 by Kenneth Russell

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/222954Reviewed-by: 's avatarKenneth Russell <kbr@chromium.org> Tested-by: 's avatarKenneth Russell <kbr@chromium.org>
parent 7c9e2167
...@@ -287,7 +287,7 @@ GLuint Context::createFenceNV() ...@@ -287,7 +287,7 @@ GLuint Context::createFenceNV()
{ {
GLuint handle = mFenceNVHandleAllocator.allocate(); GLuint handle = mFenceNVHandleAllocator.allocate();
mFenceNVMap[handle] = new FenceNV(mRenderer); mFenceNVMap[handle] = new FenceNV(mRenderer->createFenceNV());
return handle; return handle;
} }
......
...@@ -4,18 +4,8 @@ ...@@ -4,18 +4,8 @@
// found in the LICENSE file. // found in the LICENSE file.
// //
// Fence.cpp: Implements the gl::Fence class, which supports the GL_NV_fence extension. // Fence.cpp: Implements the gl::FenceNV and gl::FenceSync classes, which support the GL_NV_fence
// extension and GLES3 sync objects.
// 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.
#include "libGLESv2/Fence.h" #include "libGLESv2/Fence.h"
#include "libGLESv2/renderer/FenceImpl.h" #include "libGLESv2/renderer/FenceImpl.h"
...@@ -27,8 +17,8 @@ ...@@ -27,8 +17,8 @@
namespace gl namespace gl
{ {
FenceNV::FenceNV(rx::Renderer *renderer) FenceNV::FenceNV(rx::FenceNVImpl *impl)
: mFence(renderer->createFence()), : mFence(impl),
mIsSet(false), mIsSet(false),
mStatus(GL_FALSE), mStatus(GL_FALSE),
mCondition(GL_NONE) mCondition(GL_NONE)
...@@ -79,65 +69,14 @@ Error FenceNV::finishFence() ...@@ -79,65 +69,14 @@ Error FenceNV::finishFence()
{ {
ASSERT(mIsSet); ASSERT(mIsSet);
while (mStatus != GL_TRUE) return mFence->finishFence(&mStatus);
{
Error error = mFence->test(true, &mStatus);
if (error.isError())
{
return error;
}
Sleep(0);
}
return Error(GL_NO_ERROR);
}
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), : RefCountObject(id),
mFence(renderer->createFence()), mFence(impl),
mCounterFrequency(0),
mCondition(GL_NONE) mCondition(GL_NONE)
{ {
LARGE_INTEGER counterFreqency = { 0 };
BOOL success = QueryPerformanceFrequency(&counterFreqency);
UNUSED_ASSERTION_VARIABLE(success);
ASSERT(success);
mCounterFrequency = counterFreqency.QuadPart;
} }
FenceSync::~FenceSync() FenceSync::~FenceSync()
...@@ -160,87 +99,17 @@ Error FenceSync::set(GLenum condition) ...@@ -160,87 +99,17 @@ Error FenceSync::set(GLenum condition)
Error FenceSync::clientWait(GLbitfield flags, GLuint64 timeout, GLenum *outResult) Error FenceSync::clientWait(GLbitfield flags, GLuint64 timeout, GLenum *outResult)
{ {
ASSERT(mCondition != GL_NONE); ASSERT(mCondition != GL_NONE);
return mFence->clientWait(flags, timeout, outResult);
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(&currentCounter);
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(&currentCounter);
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);
} }
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 return mFence->serverWait(flags, timeout);
// 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);
} }
Error FenceSync::getStatus(GLint *outResult) const Error FenceSync::getStatus(GLint *outResult) const
{ {
GLboolean result = GL_FALSE; return mFence->getStatus(outResult);
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);
} }
} }
...@@ -4,7 +4,8 @@ ...@@ -4,7 +4,8 @@
// found in the LICENSE file. // 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_ #ifndef LIBGLESV2_FENCE_H_
#define LIBGLESV2_FENCE_H_ #define LIBGLESV2_FENCE_H_
...@@ -17,7 +18,8 @@ ...@@ -17,7 +18,8 @@
namespace rx namespace rx
{ {
class Renderer; class Renderer;
class FenceImpl; class FenceNVImpl;
class FenceSyncImpl;
} }
namespace gl namespace gl
...@@ -26,14 +28,13 @@ namespace gl ...@@ -26,14 +28,13 @@ namespace gl
class FenceNV class FenceNV
{ {
public: public:
explicit FenceNV(rx::Renderer *renderer); explicit FenceNV(rx::FenceNVImpl *impl);
virtual ~FenceNV(); virtual ~FenceNV();
GLboolean isFence() const; GLboolean isFence() const;
Error setFence(GLenum condition); Error setFence(GLenum condition);
Error testFence(GLboolean *outResult); Error testFence(GLboolean *outResult);
Error finishFence(); Error finishFence();
Error getFencei(GLenum pname, GLint *params);
GLboolean getStatus() const { return mStatus; } GLboolean getStatus() const { return mStatus; }
GLuint getCondition() const { return mCondition; } GLuint getCondition() const { return mCondition; }
...@@ -41,7 +42,7 @@ class FenceNV ...@@ -41,7 +42,7 @@ class FenceNV
private: private:
DISALLOW_COPY_AND_ASSIGN(FenceNV); DISALLOW_COPY_AND_ASSIGN(FenceNV);
rx::FenceImpl *mFence; rx::FenceNVImpl *mFence;
bool mIsSet; bool mIsSet;
...@@ -52,12 +53,12 @@ class FenceNV ...@@ -52,12 +53,12 @@ class FenceNV
class FenceSync : public RefCountObject class FenceSync : public RefCountObject
{ {
public: public:
explicit FenceSync(rx::Renderer *renderer, GLuint id); explicit FenceSync(rx::FenceSyncImpl *impl, GLuint id);
virtual ~FenceSync(); virtual ~FenceSync();
Error set(GLenum condition); Error set(GLenum condition);
Error clientWait(GLbitfield flags, GLuint64 timeout, GLenum *outResult); Error clientWait(GLbitfield flags, GLuint64 timeout, GLenum *outResult);
Error serverWait(); Error serverWait(GLbitfield flags, GLuint64 timeout);
Error getStatus(GLint *outResult) const; Error getStatus(GLint *outResult) const;
GLuint getCondition() const { return mCondition; } GLuint getCondition() const { return mCondition; }
...@@ -65,8 +66,7 @@ class FenceSync : public RefCountObject ...@@ -65,8 +66,7 @@ class FenceSync : public RefCountObject
private: private:
DISALLOW_COPY_AND_ASSIGN(FenceSync); DISALLOW_COPY_AND_ASSIGN(FenceSync);
rx::FenceImpl *mFence; rx::FenceSyncImpl *mFence;
LONGLONG mCounterFrequency;
GLenum mCondition; GLenum mCondition;
}; };
......
...@@ -146,7 +146,7 @@ GLuint ResourceManager::createFenceSync() ...@@ -146,7 +146,7 @@ GLuint ResourceManager::createFenceSync()
{ {
GLuint handle = mFenceSyncHandleAllocator.allocate(); GLuint handle = mFenceSyncHandleAllocator.allocate();
FenceSync *fenceSync = new FenceSync(mRenderer, handle); FenceSync *fenceSync = new FenceSync(mRenderer->createFenceSync(), handle);
fenceSync->addRef(); fenceSync->addRef();
mFenceSyncMap[handle] = fenceSync; mFenceSyncMap[handle] = fenceSync;
......
...@@ -2139,19 +2139,35 @@ void GL_APIENTRY glGetFenceivNV(GLuint fence, GLenum pname, GLint *params) ...@@ -2139,19 +2139,35 @@ void GL_APIENTRY glGetFenceivNV(GLuint fence, GLenum pname, GLint *params)
switch (pname) switch (pname)
{ {
case GL_FENCE_STATUS_NV: 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: case GL_FENCE_CONDITION_NV:
{
*params = fenceObject->getCondition();
break; break;
}
default: default:
{
context->recordError(gl::Error(GL_INVALID_ENUM)); context->recordError(gl::Error(GL_INVALID_ENUM));
return; return;
} }
gl::Error error = fenceObject->getFencei(pname, params);
if (error.isError())
{
context->recordError(error);
return;
} }
} }
} }
...@@ -7548,7 +7564,7 @@ void GL_APIENTRY glWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) ...@@ -7548,7 +7564,7 @@ void GL_APIENTRY glWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout)
return; return;
} }
gl::Error error = fenceSync->serverWait(); gl::Error error = fenceSync->serverWait(flags, timeout);
if (error.isError()) if (error.isError())
{ {
context->recordError(error); context->recordError(error);
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
// found in the LICENSE file. // 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_ #ifndef LIBGLESV2_RENDERER_FENCEIMPL_H_
#define LIBGLESV2_RENDERER_FENCEIMPL_H_ #define LIBGLESV2_RENDERER_FENCEIMPL_H_
...@@ -13,20 +13,38 @@ ...@@ -13,20 +13,38 @@
#include "common/angleutils.h" #include "common/angleutils.h"
#include "angle_gl.h"
namespace rx namespace rx
{ {
class FenceImpl class FenceNVImpl
{ {
public: public:
FenceImpl() { }; FenceNVImpl() { };
virtual ~FenceImpl() { }; virtual ~FenceNVImpl() { };
virtual gl::Error set() = 0; virtual gl::Error set() = 0;
virtual gl::Error test(bool flushCommandBuffer, GLboolean *outFinished) = 0; virtual gl::Error test(bool flushCommandBuffer, GLboolean *outFinished) = 0;
virtual gl::Error finishFence(GLboolean *outFinished) = 0;
private:
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: private:
DISALLOW_COPY_AND_ASSIGN(FenceImpl); DISALLOW_COPY_AND_ASSIGN(FenceSyncImpl);
}; };
} }
......
...@@ -50,7 +50,8 @@ class TextureStorage; ...@@ -50,7 +50,8 @@ class TextureStorage;
class VertexBuffer; class VertexBuffer;
class IndexBuffer; class IndexBuffer;
class QueryImpl; class QueryImpl;
class FenceImpl; class FenceNVImpl;
class FenceSyncImpl;
class BufferImpl; class BufferImpl;
class VertexArrayImpl; class VertexArrayImpl;
class BufferStorage; class BufferStorage;
...@@ -227,7 +228,8 @@ class Renderer ...@@ -227,7 +228,8 @@ class Renderer
// Query and Fence creation // Query and Fence creation
virtual QueryImpl *createQuery(GLenum type) = 0; virtual QueryImpl *createQuery(GLenum type) = 0;
virtual FenceImpl *createFence() = 0; virtual FenceNVImpl *createFenceNV() = 0;
virtual FenceSyncImpl *createFenceSync() = 0;
// Transform Feedback creation // Transform Feedback creation
virtual TransformFeedbackImpl* createTransformFeedback() = 0; virtual TransformFeedbackImpl* createTransformFeedback() = 0;
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
// found in the LICENSE file. // 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/Fence11.h"
#include "libGLESv2/renderer/d3d/d3d11/Renderer11.h" #include "libGLESv2/renderer/d3d/d3d11/Renderer11.h"
...@@ -13,48 +13,43 @@ ...@@ -13,48 +13,43 @@
namespace rx namespace rx
{ {
Fence11::Fence11(rx::Renderer11 *renderer) //
: mRenderer(renderer), // Template helpers for set and test operations.
mQuery(NULL) //
{
}
Fence11::~Fence11()
{
SafeRelease(mQuery);
}
gl::Error Fence11::set() template<class FenceClass>
gl::Error FenceSetHelper(FenceClass *fence)
{ {
if (!mQuery) if (!fence->mQuery)
{ {
D3D11_QUERY_DESC queryDesc; D3D11_QUERY_DESC queryDesc;
queryDesc.Query = D3D11_QUERY_EVENT; queryDesc.Query = D3D11_QUERY_EVENT;
queryDesc.MiscFlags = 0; queryDesc.MiscFlags = 0;
HRESULT result = mRenderer->getDevice()->CreateQuery(&queryDesc, &mQuery); HRESULT result = fence->mRenderer->getDevice()->CreateQuery(&queryDesc, &fence->mQuery);
if (FAILED(result)) if (FAILED(result))
{ {
return gl::Error(GL_OUT_OF_MEMORY, "Failed to create event query, result: 0x%X.", 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); 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); 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)) if (FAILED(result))
{ {
return gl::Error(GL_OUT_OF_MEMORY, "Failed to get query data, result: 0x%X.", 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."); return gl::Error(GL_OUT_OF_MEMORY, "Device was lost while querying result of an event query.");
} }
...@@ -64,4 +59,172 @@ gl::Error Fence11::test(bool flushCommandBuffer, GLboolean *outFinished) ...@@ -64,4 +59,172 @@ gl::Error Fence11::test(bool flushCommandBuffer, GLboolean *outFinished)
return gl::Error(GL_NO_ERROR); 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(&currentCounter);
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(&currentCounter);
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
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
// found in the LICENSE file. // 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_ #ifndef LIBGLESV2_RENDERER_FENCE11_H_
#define LIBGLESV2_RENDERER_Fence11_H_ #define LIBGLESV2_RENDERER_FENCE11_H_
#include "libGLESv2/renderer/FenceImpl.h" #include "libGLESv2/renderer/FenceImpl.h"
...@@ -15,20 +15,46 @@ namespace rx ...@@ -15,20 +15,46 @@ namespace rx
{ {
class Renderer11; class Renderer11;
class Fence11 : public FenceImpl class FenceNV11 : public FenceNVImpl
{ {
public: public:
explicit Fence11(rx::Renderer11 *renderer); explicit FenceNV11(Renderer11 *renderer);
virtual ~Fence11(); virtual ~FenceNV11();
gl::Error set(); gl::Error set();
gl::Error test(bool flushCommandBuffer, GLboolean *outFinished); gl::Error test(bool flushCommandBuffer, GLboolean *outFinished);
gl::Error finishFence(GLboolean *outFinished);
private: 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; ID3D11Query *mQuery;
LONGLONG mCounterFrequency;
}; };
} }
......
...@@ -2478,9 +2478,14 @@ QueryImpl *Renderer11::createQuery(GLenum type) ...@@ -2478,9 +2478,14 @@ QueryImpl *Renderer11::createQuery(GLenum type)
return new Query11(this, type); 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() TransformFeedbackImpl* Renderer11::createTransformFeedback()
......
...@@ -172,7 +172,8 @@ class Renderer11 : public Renderer ...@@ -172,7 +172,8 @@ class Renderer11 : public Renderer
// Query and Fence creation // Query and Fence creation
virtual QueryImpl *createQuery(GLenum type); virtual QueryImpl *createQuery(GLenum type);
virtual FenceImpl *createFence(); virtual FenceNVImpl *createFenceNV();
virtual FenceSyncImpl *createFenceSync();
// Transform Feedback creation // Transform Feedback creation
virtual TransformFeedbackImpl* createTransformFeedback(); virtual TransformFeedbackImpl* createTransformFeedback();
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
// found in the LICENSE file. // 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/Fence9.h"
#include "libGLESv2/renderer/d3d/d3d9/renderer9_utils.h" #include "libGLESv2/renderer/d3d/d3d9/renderer9_utils.h"
...@@ -14,18 +14,19 @@ ...@@ -14,18 +14,19 @@
namespace rx namespace rx
{ {
Fence9::Fence9(rx::Renderer9 *renderer) FenceNV9::FenceNV9(Renderer9 *renderer)
: mRenderer(renderer), : FenceNVImpl(),
mRenderer(renderer),
mQuery(NULL) mQuery(NULL)
{ {
} }
Fence9::~Fence9() FenceNV9::~FenceNV9()
{ {
SafeRelease(mQuery); SafeRelease(mQuery);
} }
gl::Error Fence9::set() gl::Error FenceNV9::set()
{ {
if (!mQuery) if (!mQuery)
{ {
...@@ -47,7 +48,7 @@ gl::Error Fence9::set() ...@@ -47,7 +48,7 @@ gl::Error Fence9::set()
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
gl::Error Fence9::test(bool flushCommandBuffer, GLboolean *outFinished) gl::Error FenceNV9::test(bool flushCommandBuffer, GLboolean *outFinished)
{ {
ASSERT(mQuery); ASSERT(mQuery);
...@@ -69,4 +70,22 @@ gl::Error Fence9::test(bool flushCommandBuffer, GLboolean *outFinished) ...@@ -69,4 +70,22 @@ gl::Error Fence9::test(bool flushCommandBuffer, GLboolean *outFinished)
return gl::Error(GL_NO_ERROR); 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);
}
} }
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
// found in the LICENSE file. // 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_ #ifndef LIBGLESV2_RENDERER_FENCE9_H_
#define LIBGLESV2_RENDERER_FENCE9_H_ #define LIBGLESV2_RENDERER_FENCE9_H_
...@@ -15,19 +15,20 @@ namespace rx ...@@ -15,19 +15,20 @@ namespace rx
{ {
class Renderer9; class Renderer9;
class Fence9 : public FenceImpl class FenceNV9 : public FenceNVImpl
{ {
public: public:
explicit Fence9(rx::Renderer9 *renderer); explicit FenceNV9(Renderer9 *renderer);
virtual ~Fence9(); virtual ~FenceNV9();
gl::Error set(); gl::Error set();
gl::Error test(bool flushCommandBuffer, GLboolean *outFinished); gl::Error test(bool flushCommandBuffer, GLboolean *outFinished);
gl::Error finishFence(GLboolean *outFinished);
private: private:
DISALLOW_COPY_AND_ASSIGN(Fence9); DISALLOW_COPY_AND_ASSIGN(FenceNV9);
rx::Renderer9 *mRenderer; Renderer9 *mRenderer;
IDirect3DQuery9 *mQuery; IDirect3DQuery9 *mQuery;
}; };
......
...@@ -637,9 +637,16 @@ QueryImpl *Renderer9::createQuery(GLenum type) ...@@ -637,9 +637,16 @@ QueryImpl *Renderer9::createQuery(GLenum type)
return new Query9(this, type); 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() TransformFeedbackImpl* Renderer9::createTransformFeedback()
......
...@@ -174,7 +174,8 @@ class Renderer9 : public Renderer ...@@ -174,7 +174,8 @@ class Renderer9 : public Renderer
// Query and Fence creation // Query and Fence creation
virtual QueryImpl *createQuery(GLenum type); virtual QueryImpl *createQuery(GLenum type);
virtual FenceImpl *createFence(); virtual FenceNVImpl *createFenceNV();
virtual FenceSyncImpl *createFenceSync();
// Transform Feedback creation // Transform Feedback creation
virtual TransformFeedbackImpl* createTransformFeedback(); virtual TransformFeedbackImpl* createTransformFeedback();
......
//
// 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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment