blob: bc1dedc7d7b0e95db21ac3e728ee386e2ecbae13 [file] [log] [blame]
//
// Copyright 2021 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.
//
// CLEventVk.cpp: Implements the class methods for CLEventVk.
#include "libANGLE/renderer/vulkan/CLEventVk.h"
#include "libANGLE/renderer/vulkan/CLCommandQueueVk.h"
#include "libANGLE/cl_utils.h"
namespace rx
{
CLEventVk::CLEventVk(const cl::Event &event,
const cl::ExecutionStatus initialStatus,
const QueueSerial eventSerial)
: CLEventImpl(event),
mStatus(cl::ToCLenum(initialStatus)),
mProfilingTimestamps(ProfilingTimestamps{}),
mQueueSerial(eventSerial)
{
ANGLE_CL_IMPL_TRY(setTimestamp(*mStatus));
}
CLEventVk::~CLEventVk() {}
angle::Result CLEventVk::onEventCreate()
{
ASSERT(!isUserEvent());
ASSERT(mQueueSerial.valid());
if (cl::FromCLenum<cl::ExecutionStatus>(*mStatus) == cl::ExecutionStatus::Complete)
{
// Submission finished at this point, just set event to complete
ANGLE_TRY(setStatusAndExecuteCallback(CL_COMPLETE));
}
else
{
getFrontendObject().getCommandQueue()->getImpl<CLCommandQueueVk>().addEventReference(*this);
if (getFrontendObject().getCommandQueue()->getProperties().intersects(
CL_QUEUE_PROFILING_ENABLE))
{
// Block for profiling so that we get timestamps per-command
ANGLE_TRY(getFrontendObject().getCommandQueue()->getImpl<CLCommandQueueVk>().finish());
}
}
return angle::Result::Continue;
}
angle::Result CLEventVk::getCommandExecutionStatus(cl_int &executionStatus)
{
executionStatus = *mStatus;
return angle::Result::Continue;
}
angle::Result CLEventVk::setUserEventStatus(cl_int executionStatus)
{
ASSERT(isUserEvent());
// Not much to do here other than storing the user supplied state.
// Error checking and single call enforcement is responsibility of the front end.
ANGLE_TRY(setStatusAndExecuteCallback(executionStatus));
// User event set and callback(s) finished - notify those waiting
mUserEventCondition.notify_all();
return angle::Result::Continue;
}
angle::Result CLEventVk::setCallback(cl::Event &event, cl_int commandExecCallbackType)
{
ASSERT(commandExecCallbackType >= CL_COMPLETE);
ASSERT(commandExecCallbackType < CL_QUEUED);
// Not much to do, acknowledge the presence of callback and returns
mHaveCallbacks->at(commandExecCallbackType) = true;
return angle::Result::Continue;
}
angle::Result CLEventVk::getProfilingInfo(cl::ProfilingInfo name,
size_t valueSize,
void *value,
size_t *valueSizeRet)
{
cl_ulong valueUlong = 0;
size_t copySize = 0;
const void *copyValue = nullptr;
auto profilingTimestamps = mProfilingTimestamps.synchronize();
switch (name)
{
case cl::ProfilingInfo::CommandQueued:
valueUlong = profilingTimestamps->commandQueuedTS;
break;
case cl::ProfilingInfo::CommandSubmit:
valueUlong = profilingTimestamps->commandSubmitTS;
break;
case cl::ProfilingInfo::CommandStart:
valueUlong = profilingTimestamps->commandStartTS;
break;
case cl::ProfilingInfo::CommandEnd:
valueUlong = profilingTimestamps->commandEndTS;
break;
case cl::ProfilingInfo::CommandComplete:
valueUlong = profilingTimestamps->commandCompleteTS;
break;
default:
UNREACHABLE();
}
copyValue = &valueUlong;
copySize = sizeof(valueUlong);
if ((value != nullptr) && (copyValue != nullptr))
{
memcpy(value, copyValue, std::min(valueSize, copySize));
}
if (valueSizeRet != nullptr)
{
*valueSizeRet = copySize;
}
return angle::Result::Continue;
}
angle::Result CLEventVk::waitForUserEventStatus()
{
ASSERT(isUserEvent());
cl_int status = CL_QUEUED;
std::unique_lock<std::mutex> ul(mUserEventMutex);
ANGLE_TRY(getCommandExecutionStatus(status));
if (status > CL_COMPLETE)
{
// User is responsible for setting the user-event object, we need to wait for that event
// (We dont care what the outcome is, just need to wait until that event triggers)
INFO() << "Waiting for user-event (" << &mEvent
<< ") to be set! (aka clSetUserEventStatus)";
mUserEventCondition.wait(ul);
}
return angle::Result::Continue;
}
angle::Result CLEventVk::setStatusAndExecuteCallback(cl_int status)
{
auto statusHandle = mStatus.synchronize();
auto haveCallbacksHandle = mHaveCallbacks.synchronize();
// we might skip states in some cases i.e. move from QUEUED to COMPLETE, so
// make sure we are setting time stamps for all transitions
ASSERT(*statusHandle >= status);
while (*statusHandle > status)
{
(*statusHandle)--;
ANGLE_TRY(setTimestamp(*statusHandle));
if (*statusHandle >= CL_COMPLETE && *statusHandle < CL_QUEUED &&
haveCallbacksHandle->at(*statusHandle))
{
getFrontendObject().callback(*statusHandle);
haveCallbacksHandle->at(*statusHandle) = false;
}
}
return angle::Result::Continue;
}
angle::Result CLEventVk::setTimestamp(cl_int status)
{
if (!isUserEvent() &&
mEvent.getCommandQueue()->getProperties().intersects(CL_QUEUE_PROFILING_ENABLE))
{
// TODO(aannestrand) Just get current CPU timestamp for now, look into Vulkan GPU device
// timestamp query instead and later make CPU timestamp a fallback if GPU timestamp cannot
// be queried http://anglebug.com/357902514
cl_ulong cpuTS =
std::chrono::time_point_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now())
.time_since_epoch()
.count();
auto profilingTimestamps = mProfilingTimestamps.synchronize();
switch (status)
{
case CL_QUEUED:
profilingTimestamps->commandQueuedTS = cpuTS;
break;
case CL_SUBMITTED:
profilingTimestamps->commandSubmitTS = cpuTS;
break;
case CL_RUNNING:
profilingTimestamps->commandStartTS = cpuTS;
break;
case CL_COMPLETE:
profilingTimestamps->commandEndTS = cpuTS;
// Returns a value equivalent to passing CL_PROFILING_COMMAND_END if the device
// associated with event does not support device-side enqueue.
// https://registry.khronos.org/OpenCL/specs/3.0-unified/html/OpenCL_API.html#_device_side_enqueue
profilingTimestamps->commandCompleteTS = cpuTS;
break;
default:
UNREACHABLE();
}
}
return angle::Result::Continue;
}
} // namespace rx