Commit 82fddcb1 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Implement GLsync and EGLSync fence syncs

That is required in GLES 3 for GLsync and EGL_KHR_fence_sync and EGL_KHR_wait_sync (or EGL 1.5) for EGLSync. The two constructs (GLsync and EGLSync) have similar semantics and share the implementation on the Vulkan backend. The implementation of a fence sync object is achieved through the combined use of a vkEvent and the implicit vkFence inserted at the end of every submission. Imagine the following command buffer: glDraw : Draw glCreateSync: Set Event <-- insertion of fence sync glDraw : Draw : Signal Fence <-- implicit fence at the end of submission glFlush : Submit Assume the serial S is associated to this submission. The following hold: - If event is set, the fence sync is signaled - If S is already finished, the fence sync is signaled - If client is waiting on the sync and S is not yet flushed, there will be a deadlock (unless multi-threaded and another thread performs the flush). The event is used to implement server waits (glWaitSync), as vkEvent is the only entity the GPU can signal and wait on within the command buffer. The wait is inserted in the command graph without incurring a flush, i.e. the wait can be within the same command buffer as event set. The event however does not support CPU waits (glClientWaitSync). vkFence is the only entity the CPU can wait on. For client wait therefore, the following algorithm is used: - If the event is already set, there's no wait -> already signaled - If timeout is zero, there's no wait -> timeout expired - If S is not flushed, flush it to ensure forward progress. - Wait until S is finished -> condition satisfied / timeout expired. Bug: angleproject:2466 Change-Id: I678995a6139dd9533fa8ad361a3d292b202c52a4 Reviewed-on: https://chromium-review.googlesource.com/c/1422552 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 4b2e00f4
......@@ -74,6 +74,17 @@ const char *GetResourceTypeName(CommandGraphResourceType resourceType,
UNREACHABLE();
return "Query";
}
case CommandGraphResourceType::FenceSync:
switch (function)
{
case CommandGraphNodeFunction::SetFenceSync:
return "SetFenceSync";
case CommandGraphNodeFunction::WaitFenceSync:
return "WaitFenceSync";
default:
UNREACHABLE();
return "FenceSync";
}
default:
UNREACHABLE();
return "";
......@@ -211,6 +222,7 @@ CommandGraphNode::CommandGraphNode(CommandGraphNodeFunction function)
mFunction(function),
mQueryPool(VK_NULL_HANDLE),
mQueryIndex(0),
mFenceSyncEvent(VK_NULL_HANDLE),
mHasChildren(false),
mVisitedState(VisitedState::Unvisited),
mGlobalMemoryBarrierSrcAccess(0),
......@@ -337,6 +349,13 @@ void CommandGraphNode::setQueryPool(const QueryPool *queryPool, uint32_t queryIn
mQueryIndex = queryIndex;
}
void CommandGraphNode::setFenceSync(const vk::Event &event)
{
ASSERT(mFunction == CommandGraphNodeFunction::SetFenceSync ||
mFunction == CommandGraphNodeFunction::WaitFenceSync);
mFenceSyncEvent = event.getHandle();
}
// Do not call this in anything but testing code, since it's slow.
bool CommandGraphNode::isChildOf(CommandGraphNode *parent)
{
......@@ -381,7 +400,7 @@ angle::Result CommandGraphNode::visitAndExecute(vk::Context *context,
switch (mFunction)
{
case CommandGraphNodeFunction::Generic:
ASSERT(mQueryPool == VK_NULL_HANDLE);
ASSERT(mQueryPool == VK_NULL_HANDLE && mFenceSyncEvent == VK_NULL_HANDLE);
// Record the deferred pipeline barrier if necessary.
ASSERT((mGlobalMemoryBarrierDstAccess == 0) == (mGlobalMemoryBarrierSrcAccess == 0));
......@@ -461,6 +480,25 @@ angle::Result CommandGraphNode::visitAndExecute(vk::Context *context,
break;
case CommandGraphNodeFunction::SetFenceSync:
ASSERT(!mOutsideRenderPassCommands.valid() && !mInsideRenderPassCommands.valid());
ASSERT(mFenceSyncEvent != VK_NULL_HANDLE);
primaryCommandBuffer->setEvent(mFenceSyncEvent, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
break;
case CommandGraphNodeFunction::WaitFenceSync:
ASSERT(!mOutsideRenderPassCommands.valid() && !mInsideRenderPassCommands.valid());
ASSERT(mFenceSyncEvent != VK_NULL_HANDLE);
// Fence Syncs are purely execution barriers, so there are no memory barriers attached.
primaryCommandBuffer->waitEvents(
1, &mFenceSyncEvent, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, nullptr, 0, nullptr, 0, nullptr);
break;
default:
UNREACHABLE();
}
......@@ -660,6 +698,20 @@ void CommandGraph::writeTimestamp(const QueryPool *queryPool, uint32_t queryInde
newNode->setQueryPool(queryPool, queryIndex);
}
void CommandGraph::setFenceSync(const vk::Event &event)
{
CommandGraphNode *newNode = allocateBarrierNode(CommandGraphResourceType::FenceSync,
CommandGraphNodeFunction::SetFenceSync);
newNode->setFenceSync(event);
}
void CommandGraph::waitFenceSync(const vk::Event &event)
{
CommandGraphNode *newNode = allocateBarrierNode(CommandGraphResourceType::FenceSync,
CommandGraphNodeFunction::WaitFenceSync);
newNode->setFenceSync(event);
}
// Dumps the command graph into a dot file that works with graphviz.
void CommandGraph::dumpGraphDotFile(std::ostream &out) const
{
......
......@@ -30,6 +30,7 @@ enum class CommandGraphResourceType
Framebuffer,
Image,
Query,
FenceSync,
};
// Certain functionality cannot be put in secondary command buffers, so they are special-cased in
......@@ -40,6 +41,8 @@ enum class CommandGraphNodeFunction
BeginQuery,
EndQuery,
WriteTimestamp,
SetFenceSync,
WaitFenceSync,
};
// Receives notifications when a command buffer is no longer able to record. Can be used with
......@@ -127,6 +130,7 @@ class CommandGraphNode final : angle::NonCopyable
CommandGraphNodeFunction getFunction() const { return mFunction; }
void setQueryPool(const QueryPool *queryPool, uint32_t queryIndex);
void setFenceSync(const vk::Event &event);
ANGLE_INLINE void addGlobalMemoryBarrier(VkFlags srcAccess, VkFlags dstAccess)
{
......@@ -168,8 +172,11 @@ class CommandGraphNode final : angle::NonCopyable
CommandBuffer mInsideRenderPassCommands;
// Special-function additional data:
// Queries:
VkQueryPool mQueryPool;
uint32_t mQueryIndex;
// GLsync and EGLSync:
VkEvent mFenceSyncEvent;
// Parents are commands that must be submitted before 'this' CommandNode can be submitted.
std::vector<CommandGraphNode *> mParents;
......@@ -359,6 +366,9 @@ class CommandGraph final : angle::NonCopyable
void beginQuery(const QueryPool *queryPool, uint32_t queryIndex);
void endQuery(const QueryPool *queryPool, uint32_t queryIndex);
void writeTimestamp(const QueryPool *queryPool, uint32_t queryIndex);
// GLsync and EGLSync:
void setFenceSync(const vk::Event &event);
void waitFenceSync(const vk::Event &event);
private:
CommandGraphNode *allocateBarrierNode(CommandGraphResourceType resourceType,
......
......@@ -15,6 +15,7 @@
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/RendererVk.h"
#include "libANGLE/renderer/vulkan/SurfaceVk.h"
#include "libANGLE/renderer/vulkan/SyncVk.h"
namespace rx
{
......@@ -80,10 +81,6 @@ DeviceImpl *DisplayVk::createDevice()
egl::Error DisplayVk::waitClient(const gl::Context *context)
{
// TODO(jmadill): Call flush instead of finish once it is implemented in RendererVK.
// http://anglebug.com/2504
UNIMPLEMENTED();
return angle::ToEGL(mRenderer->finish(this), this, EGL_BAD_ACCESS);
}
......@@ -157,6 +154,11 @@ StreamProducerImpl *DisplayVk::createStreamProducerD3DTexture(
return static_cast<StreamProducerImpl *>(0);
}
EGLSyncImpl *DisplayVk::createSync(const egl::AttributeMap &attribs)
{
return new EGLSyncVk(attribs);
}
gl::Version DisplayVk::getMaxSupportedESVersion() const
{
return mRenderer->getMaxSupportedESVersion();
......@@ -176,6 +178,9 @@ void DisplayVk::generateExtensions(egl::DisplayExtensions *outExtensions) const
// When the Vulkan driver supports VK_KHR_incremental_present, it will use it. Otherwise, it
// will ignore the hint and do a regular swap.
outExtensions->swapBuffersWithDamage = true;
outExtensions->fenceSync = true;
outExtensions->waitSync = true;
}
void DisplayVk::generateCaps(egl::Caps *outCaps) const
......
......@@ -67,6 +67,9 @@ class DisplayVk : public DisplayImpl, public vk::Context
StreamProducerImpl *createStreamProducerD3DTexture(egl::Stream::ConsumerType consumerType,
const egl::AttributeMap &attribs) override;
EGLSyncImpl *createSync(const egl::AttributeMap &attribs) override;
gl::Version getMaxSupportedESVersion() const override;
virtual const char *getWSIName() const = 0;
......
......@@ -53,9 +53,9 @@ namespace
// one for the vertex shader.
constexpr size_t kUniformBufferDescriptorsPerDescriptorSet = 2;
// Update the pipeline cache every this many swaps (if 60fps, this means every 10 minutes)
static constexpr uint32_t kPipelineCacheVkUpdatePeriod = 10 * 60 * 60;
constexpr uint32_t kPipelineCacheVkUpdatePeriod = 10 * 60 * 60;
// Wait a maximum of 10s. If that times out, we declare it a failure.
static constexpr uint64_t kMaxFenceWaitTimeNs = 10'000'000'000llu;
constexpr uint64_t kMaxFenceWaitTimeNs = 10'000'000'000llu;
bool ShouldEnableMockICD(const egl::AttributeMap &attribs)
{
......@@ -1364,6 +1364,24 @@ bool RendererVk::isSerialInUse(Serial serial) const
angle::Result RendererVk::finishToSerial(vk::Context *context, Serial serial)
{
bool timedOut = false;
angle::Result result = finishToSerialOrTimeout(context, serial, kMaxFenceWaitTimeNs, &timedOut);
// Don't tolerate timeout. If such a large wait time results in timeout, something's wrong.
if (timedOut)
{
result = angle::Result::Stop;
}
return result;
}
angle::Result RendererVk::finishToSerialOrTimeout(vk::Context *context,
Serial serial,
uint64_t timeout,
bool *outTimedOut)
{
*outTimedOut = false;
if (!isSerialInUse(serial) || mInFlightCommands.empty())
{
return angle::Result::Continue;
......@@ -1383,7 +1401,16 @@ angle::Result RendererVk::finishToSerial(vk::Context *context, Serial serial)
const CommandBatch &batch = mInFlightCommands[batchIndex];
// Wait for it finish
ANGLE_VK_TRY(context, batch.fence.wait(mDevice, kMaxFenceWaitTimeNs));
VkResult status = batch.fence.wait(mDevice, kMaxFenceWaitTimeNs);
// If timed out, report it as such.
if (status == VK_TIMEOUT)
{
*outTimedOut = true;
return angle::Result::Continue;
}
ANGLE_VK_TRY(context, status);
// Clean up finished batches.
return checkCompletedCommands(context);
......@@ -1785,7 +1812,7 @@ angle::Result RendererVk::synchronizeCpuGpuTime(vk::Context *context)
ANGLE_VK_TRY(context, commandBuffer.begin(beginInfo));
commandBuffer.setEvent(gpuReady.get(), VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
commandBuffer.setEvent(gpuReady.get().getHandle(), VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
commandBuffer.waitEvents(1, cpuReady.get().ptr(), VK_PIPELINE_STAGE_HOST_BIT,
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, nullptr, 0, nullptr, 0,
nullptr);
......@@ -1796,7 +1823,7 @@ angle::Result RendererVk::synchronizeCpuGpuTime(vk::Context *context)
timestampQuery.getQueryPool()->getHandle(),
timestampQuery.getQuery());
commandBuffer.setEvent(gpuDone.get(), VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
commandBuffer.setEvent(gpuDone.get().getHandle(), VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
ANGLE_VK_TRY(context, commandBuffer.end());
......
......@@ -107,8 +107,14 @@ class RendererVk : angle::NonCopyable
// mLastCompletedQueueSerial, for example for when the application busy waits on a query
// result).
angle::Result checkCompletedCommands(vk::Context *context);
// Wait for completion of batches until (at least) batch with given serial is finished.
angle::Result finishToSerial(vk::Context *context, Serial serial);
// A variant of finishToSerial that can time out. Timeout status returned in outTimedOut.
angle::Result finishToSerialOrTimeout(vk::Context *context,
Serial serial,
uint64_t timeout,
bool *outTimedOut);
uint32_t getQueueFamilyIndex() const { return mCurrentQueueFamilyIndex; }
......
......@@ -11,20 +11,125 @@
#include "common/debug.h"
#include "libANGLE/Context.h"
#include "libANGLE/Display.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/vk_utils.h"
#include "libANGLE/renderer/vulkan/DisplayVk.h"
namespace rx
{
FenceSyncVk::FenceSyncVk() {}
FenceSyncVk::~FenceSyncVk() {}
void FenceSyncVk::onDestroy(RendererVk *renderer)
{
if (mEvent.valid())
{
renderer->releaseObject(renderer->getCurrentQueueSerial(), &mEvent);
}
}
angle::Result FenceSyncVk::initialize(vk::Context *context)
{
ASSERT(!mEvent.valid());
RendererVk *renderer = context->getRenderer();
VkDevice device = renderer->getDevice();
VkEventCreateInfo eventCreateInfo = {};
eventCreateInfo.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
eventCreateInfo.flags = 0;
vk::Scoped<vk::Event> event(device);
ANGLE_VK_TRY(context, event.get().init(device, eventCreateInfo));
mEvent = event.release();
mSignalSerial = renderer->getCurrentQueueSerial();
renderer->getCommandGraph()->setFenceSync(mEvent);
return angle::Result::Continue;
}
angle::Result FenceSyncVk::clientWait(vk::Context *context,
bool flushCommands,
uint64_t timeout,
VkResult *outResult)
{
RendererVk *renderer = context->getRenderer();
// If the event is already set, don't wait
bool alreadySignaled = false;
ANGLE_TRY(getStatus(context, &alreadySignaled));
if (alreadySignaled)
{
*outResult = VK_EVENT_SET;
return angle::Result::Continue;
}
// If timeout is zero, there's no need to wait, so return timeout already.
if (timeout == 0)
{
*outResult = VK_TIMEOUT;
return angle::Result::Continue;
}
// If asked to flush, only do so if the queue serial hasn't changed (as otherwise the event
// signal is already flushed). If not asked to flush, do the flush anyway! This is because
// there's no cpu-side wait on the event and there's no fence yet inserted to wait on. We could
// test the event in a loop with a sleep, which can only ever not timeout if another thread
// performs the flush. Instead, we perform the flush for simplicity.
if (hasPendingWork(renderer))
{
ANGLE_TRY(renderer->flush(context));
}
// Wait on the fence that's implicitly inserted at the end of every submission.
bool timedOut = false;
angle::Result result =
renderer->finishToSerialOrTimeout(context, mSignalSerial, timeout, &timedOut);
ANGLE_TRY(result);
*outResult = timedOut ? VK_TIMEOUT : VK_SUCCESS;
return angle::Result::Continue;
}
angle::Result FenceSyncVk::serverWait(vk::Context *context)
{
context->getRenderer()->getCommandGraph()->waitFenceSync(mEvent);
return angle::Result::Continue;
}
angle::Result FenceSyncVk::getStatus(vk::Context *context, bool *signaled)
{
VkResult result = mEvent.getStatus(context->getRenderer()->getDevice());
if (result != VK_EVENT_SET && result != VK_EVENT_RESET)
{
ANGLE_VK_TRY(context, result);
}
*signaled = result == VK_EVENT_SET;
return angle::Result::Continue;
}
bool FenceSyncVk::hasPendingWork(RendererVk *renderer)
{
return mSignalSerial == renderer->getCurrentQueueSerial();
}
SyncVk::SyncVk() : SyncImpl() {}
SyncVk::~SyncVk() {}
void SyncVk::onDestroy(const gl::Context *context)
{
mFenceSync.onDestroy(vk::GetImpl(context)->getRenderer());
}
angle::Result SyncVk::set(const gl::Context *context, GLenum condition, GLbitfield flags)
{
ANGLE_VK_UNREACHABLE(vk::GetImpl(context));
return angle::Result::Stop;
ASSERT(condition == GL_SYNC_GPU_COMMANDS_COMPLETE);
ASSERT(flags == 0);
return mFenceSync.initialize(vk::GetImpl(context));
}
angle::Result SyncVk::clientWait(const gl::Context *context,
......@@ -32,20 +137,133 @@ angle::Result SyncVk::clientWait(const gl::Context *context,
GLuint64 timeout,
GLenum *outResult)
{
ANGLE_VK_UNREACHABLE(vk::GetImpl(context));
return angle::Result::Stop;
ContextVk *contextVk = vk::GetImpl(context);
ASSERT((flags & ~GL_SYNC_FLUSH_COMMANDS_BIT) == 0);
bool flush = (flags & GL_SYNC_FLUSH_COMMANDS_BIT) != 0;
VkResult result;
ANGLE_TRY(mFenceSync.clientWait(contextVk, flush, static_cast<uint64_t>(timeout), &result));
switch (result)
{
case VK_EVENT_SET:
*outResult = GL_ALREADY_SIGNALED;
return angle::Result::Continue;
case VK_SUCCESS:
*outResult = GL_CONDITION_SATISFIED;
return angle::Result::Continue;
case VK_TIMEOUT:
*outResult = GL_TIMEOUT_EXPIRED;
return angle::Result::Incomplete;
default:
UNREACHABLE();
*outResult = GL_WAIT_FAILED;
return angle::Result::Stop;
}
}
angle::Result SyncVk::serverWait(const gl::Context *context, GLbitfield flags, GLuint64 timeout)
{
ANGLE_VK_UNREACHABLE(vk::GetImpl(context));
return angle::Result::Stop;
ASSERT(flags == 0);
ASSERT(timeout == GL_TIMEOUT_IGNORED);
return mFenceSync.serverWait(vk::GetImpl(context));
}
angle::Result SyncVk::getStatus(const gl::Context *context, GLint *outResult)
{
ANGLE_VK_UNREACHABLE(vk::GetImpl(context));
return angle::Result::Stop;
bool signaled = false;
ANGLE_TRY(mFenceSync.getStatus(vk::GetImpl(context), &signaled));
*outResult = signaled ? GL_SIGNALED : GL_UNSIGNALED;
return angle::Result::Continue;
}
EGLSyncVk::EGLSyncVk(const egl::AttributeMap &attribs) : EGLSyncImpl()
{
ASSERT(attribs.isEmpty());
}
EGLSyncVk::~EGLSyncVk() {}
void EGLSyncVk::onDestroy(const egl::Display *display)
{
mFenceSync.onDestroy(vk::GetImpl(display)->getRenderer());
}
egl::Error EGLSyncVk::initialize(const egl::Display *display, EGLenum type)
{
ASSERT(type == EGL_SYNC_FENCE_KHR);
if (mFenceSync.initialize(vk::GetImpl(display)) == angle::Result::Stop)
{
return egl::Error(EGL_BAD_ALLOC, "eglCreateSyncKHR failed to create sync object");
}
return egl::NoError();
}
egl::Error EGLSyncVk::clientWait(const egl::Display *display,
EGLint flags,
EGLTime timeout,
EGLint *outResult)
{
ASSERT((flags & ~EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) == 0);
bool flush = (flags & EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) != 0;
VkResult result;
if (mFenceSync.clientWait(vk::GetImpl(display), flush, static_cast<uint64_t>(timeout),
&result) == angle::Result::Stop)
{
return egl::Error(EGL_BAD_ALLOC);
}
switch (result)
{
case VK_EVENT_SET:
// fall through. EGL doesn't differentiate between event being already set, or set
// before timeout.
case VK_SUCCESS:
*outResult = EGL_CONDITION_SATISFIED_KHR;
return egl::NoError();
case VK_TIMEOUT:
*outResult = EGL_TIMEOUT_EXPIRED_KHR;
return egl::NoError();
default:
UNREACHABLE();
*outResult = EGL_FALSE;
return egl::Error(EGL_BAD_ALLOC);
};
}
egl::Error EGLSyncVk::serverWait(const egl::Display *display, EGLint flags)
{
ASSERT(flags == 0);
if (mFenceSync.serverWait(vk::GetImpl(display)) == angle::Result::Stop)
{
return egl::Error(EGL_BAD_ALLOC);
}
return egl::NoError();
}
egl::Error EGLSyncVk::getStatus(const egl::Display *display, EGLint *outStatus)
{
bool signaled = false;
if (mFenceSync.getStatus(vk::GetImpl(display), &signaled) == angle::Result::Stop)
{
return egl::Error(EGL_BAD_ALLOC);
}
*outStatus = signaled ? EGL_SIGNALED_KHR : EGL_UNSIGNALED_KHR;
return egl::NoError();
}
} // namespace rx
......@@ -10,16 +10,56 @@
#ifndef LIBANGLE_RENDERER_VULKAN_FENCESYNCVK_H_
#define LIBANGLE_RENDERER_VULKAN_FENCESYNCVK_H_
#include "libANGLE/renderer/EGLSyncImpl.h"
#include "libANGLE/renderer/SyncImpl.h"
#include "libANGLE/renderer/vulkan/vk_utils.h"
namespace egl
{
class AttributeMap;
}
namespace rx
{
class SyncVk : public SyncImpl
// The behaviors of SyncImpl and EGLSyncImpl as fence syncs (only supported type) are currently
// identical for the Vulkan backend, and this class implements both interfaces.
class FenceSyncVk
{
public:
FenceSyncVk();
~FenceSyncVk();
void onDestroy(RendererVk *renderer);
angle::Result initialize(vk::Context *context);
angle::Result clientWait(vk::Context *context,
bool flushCommands,
uint64_t timeout,
VkResult *outResult);
angle::Result serverWait(vk::Context *context);
angle::Result getStatus(vk::Context *context, bool *signaled);
private:
bool hasPendingWork(RendererVk *renderer);
// The vkEvent that's signaled on `init` and can be waited on in `serverWait`, or queried with
// `getStatus`.
vk::Event mEvent;
// The serial in which the event was inserted. Used in `clientWait` to know whether flush is
// necessary, and to be able to wait on the vkFence that's automatically inserted at the end of
// each submissions.
Serial mSignalSerial;
};
class SyncVk final : public SyncImpl
{
public:
SyncVk();
~SyncVk() override;
void onDestroy(const gl::Context *context) override;
angle::Result set(const gl::Context *context, GLenum condition, GLbitfield flags) override;
angle::Result clientWait(const gl::Context *context,
GLbitfield flags,
......@@ -29,6 +69,29 @@ class SyncVk : public SyncImpl
GLbitfield flags,
GLuint64 timeout) override;
angle::Result getStatus(const gl::Context *context, GLint *outResult) override;
private:
FenceSyncVk mFenceSync;
};
class EGLSyncVk final : public EGLSyncImpl
{
public:
EGLSyncVk(const egl::AttributeMap &attribs);
~EGLSyncVk() override;
void onDestroy(const egl::Display *display) override;
egl::Error initialize(const egl::Display *display, EGLenum type) override;
egl::Error clientWait(const egl::Display *display,
EGLint flags,
EGLTime timeout,
EGLint *outResult) override;
egl::Error serverWait(const egl::Display *display, EGLint flags) override;
egl::Error getStatus(const egl::Display *display, EGLint *outStatus) override;
private:
FenceSyncVk mFenceSync;
};
} // namespace rx
......
......@@ -53,6 +53,8 @@ void GenerateCaps(const VkPhysicalDeviceProperties &physicalDeviceProperties,
// Only expose robust buffer access if the physical device supports it.
outExtensions->robustBufferAccessBehavior = physicalDeviceFeatures.robustBufferAccess;
outExtensions->eglSync = true;
// We use secondary command buffers almost everywhere and they require a feature to be
// able to execute in the presence of queries. As a result, we won't support queries
// unless that feature is available.
......
......@@ -511,16 +511,16 @@ void CommandBuffer::pushConstants(const PipelineLayout &layout,
vkCmdPushConstants(mHandle, layout.getHandle(), flag, offset, size, data);
}
void CommandBuffer::setEvent(const vk::Event &event, VkPipelineStageFlags stageMask)
void CommandBuffer::setEvent(VkEvent event, VkPipelineStageFlags stageMask)
{
ASSERT(valid() && event.valid());
vkCmdSetEvent(mHandle, event.getHandle(), stageMask);
ASSERT(event != VK_NULL_HANDLE);
vkCmdSetEvent(mHandle, event, stageMask);
}
void CommandBuffer::resetEvent(const vk::Event &event, VkPipelineStageFlags stageMask)
void CommandBuffer::resetEvent(VkEvent event, VkPipelineStageFlags stageMask)
{
ASSERT(valid() && event.valid());
vkCmdResetEvent(mHandle, event.getHandle(), stageMask);
ASSERT(event != VK_NULL_HANDLE);
vkCmdResetEvent(mHandle, event, stageMask);
}
void CommandBuffer::waitEvents(uint32_t eventCount,
......
......@@ -455,8 +455,8 @@ class CommandBuffer : public WrappedObject<CommandBuffer, VkCommandBuffer>
uint32_t size,
const void *data);
void setEvent(const vk::Event &event, VkPipelineStageFlags stageMask);
void resetEvent(const vk::Event &event, VkPipelineStageFlags stageMask);
void setEvent(VkEvent event, VkPipelineStageFlags stageMask);
void resetEvent(VkEvent event, VkPipelineStageFlags stageMask);
void waitEvents(uint32_t eventCount,
const VkEvent *events,
VkPipelineStageFlags srcStageMask,
......
......@@ -270,4 +270,4 @@ ANGLE_INSTANTIATE_TEST(FenceNVTest,
ES2_OPENGLES(),
ES3_OPENGLES(),
ES2_VULKAN());
ANGLE_INSTANTIATE_TEST(FenceSyncTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
ANGLE_INSTANTIATE_TEST(FenceSyncTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES(), ES3_VULKAN());
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