Commit 44a5c913 by John Plate Committed by Angle LUCI CQ

CL: Make CL front end and back end thread-safe

Add locking to all mutable variables of the CL objects in the front end and pass-through back end to make them thread-safe. This fixes a crash in a multi-threaded CTS test. Bug: angleproject:6015 Change-Id: I1d6471c851217639411c434c82acd32d14035291 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2967468 Commit-Queue: John Plate <jplate@google.com> Reviewed-by: 's avatarCody Northrop <cnorthrop@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 7753c837
//
// 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.
//
// Spinlock.h:
// Spinlock is a lock that loops actively until it gets the resource.
// Only use it when the lock will be granted in reasonably short time.
#ifndef COMMON_SPINLOCK_H_
#define COMMON_SPINLOCK_H_
#include <atomic>
// TODO(jplate) Add pause for ARM, http://anglebug.com:6067
#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
extern "C" void _mm_pause();
# pragma intrinsic(_mm_pause)
# define ANGLE_SMT_PAUSE() _mm_pause()
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
# define ANGLE_SMT_PAUSE() __asm__ __volatile__("pause;")
#else
# define ANGLE_SMT_PAUSE() static_cast<void>(0)
#endif
namespace angle
{
class Spinlock
{
public:
Spinlock() noexcept;
bool try_lock() noexcept;
void lock() noexcept;
void unlock() noexcept;
private:
std::atomic_bool mLock;
};
inline Spinlock::Spinlock() noexcept : mLock(false) {}
inline bool Spinlock::try_lock() noexcept
{
// Relaxed check first to prevent unnecessary cache misses.
return !mLock.load(std::memory_order_relaxed) &&
!mLock.exchange(true, std::memory_order_acquire);
}
inline void Spinlock::lock() noexcept
{
while (mLock.exchange(true, std::memory_order_acquire))
{
// Relaxed wait to prevent unnecessary cache misses.
while (mLock.load(std::memory_order_relaxed))
{
// Optimization for simultaneous multithreading.
ANGLE_SMT_PAUSE();
}
}
}
inline void Spinlock::unlock() noexcept
{
mLock.store(false, std::memory_order_release);
}
} // namespace angle
#endif // COMMON_SPINLOCK_H_
......@@ -43,10 +43,11 @@ cl_int CommandQueue::getInfo(CommandQueueInfo name,
void *value,
size_t *valueSizeRet) const
{
cl_uint valUInt = 0u;
void *valPointer = nullptr;
const void *copyValue = nullptr;
size_t copySize = 0u;
cl_command_queue_properties properties = 0u;
cl_uint valUInt = 0u;
void *valPointer = nullptr;
const void *copyValue = nullptr;
size_t copySize = 0u;
switch (name)
{
......@@ -66,8 +67,9 @@ cl_int CommandQueue::getInfo(CommandQueueInfo name,
copySize = sizeof(valUInt);
break;
case CommandQueueInfo::Properties:
copyValue = &mProperties;
copySize = sizeof(mProperties);
properties = mProperties->get();
copyValue = &properties;
copySize = sizeof(properties);
break;
case CommandQueueInfo::PropertiesArray:
copyValue = mPropArray.data();
......@@ -78,7 +80,7 @@ cl_int CommandQueue::getInfo(CommandQueueInfo name,
copySize = sizeof(mSize);
break;
case CommandQueueInfo::DeviceDefault:
valPointer = CommandQueue::CastNative(mDevice->mDefaultCommandQueue);
valPointer = CommandQueue::CastNative(*mDevice->mDefaultCommandQueue);
copyValue = &valPointer;
copySize = sizeof(valPointer);
break;
......@@ -110,23 +112,23 @@ cl_int CommandQueue::setProperty(CommandQueueProperties properties,
cl_bool enable,
cl_command_queue_properties *oldProperties)
{
auto props = mProperties.synchronize();
if (oldProperties != nullptr)
{
*oldProperties = mProperties.get();
*oldProperties = props->get();
}
const cl_int result = mImpl->setProperty(properties, enable);
if (result == CL_SUCCESS)
ANGLE_CL_TRY(mImpl->setProperty(properties, enable));
if (enable == CL_FALSE)
{
if (enable == CL_FALSE)
{
mProperties.clear(properties);
}
else
{
mProperties.set(properties);
}
props->clear(properties);
}
else
{
props->set(properties);
}
return result;
return CL_SUCCESS;
}
cl_int CommandQueue::enqueueReadBuffer(cl_mem buffer,
......@@ -695,9 +697,10 @@ cl_int CommandQueue::finish()
CommandQueue::~CommandQueue()
{
if (mDevice->mDefaultCommandQueue == this)
auto queue = mDevice->mDefaultCommandQueue.synchronize();
if (*queue == this)
{
mDevice->mDefaultCommandQueue = nullptr;
*queue = nullptr;
}
}
......@@ -720,9 +723,9 @@ CommandQueue::CommandQueue(Context &context,
mSize(size),
mImpl(context.getImpl().createCommandQueue(*this, errorCode))
{
if (mProperties.isSet(CL_QUEUE_ON_DEVICE_DEFAULT))
if (mProperties->isSet(CL_QUEUE_ON_DEVICE_DEFAULT))
{
mDevice->mDefaultCommandQueue = this;
*mDevice->mDefaultCommandQueue = this;
}
}
......
......@@ -12,6 +12,8 @@
#include "libANGLE/CLObject.h"
#include "libANGLE/renderer/CLCommandQueueImpl.h"
#include "common/SynchronizedValue.h"
#include <limits>
namespace cl
......@@ -281,7 +283,7 @@ class CommandQueue final : public _cl_command_queue, public Object
const ContextPtr mContext;
const DevicePtr mDevice;
const PropArray mPropArray;
CommandQueueProperties mProperties;
angle::SynchronizedValue<CommandQueueProperties> mProperties;
const cl_uint mSize = kNoSize;
const rx::CLCommandQueueImpl::Ptr mImpl;
......@@ -305,17 +307,17 @@ inline const Device &CommandQueue::getDevice() const
inline CommandQueueProperties CommandQueue::getProperties() const
{
return mProperties;
return *mProperties;
}
inline bool CommandQueue::isOnHost() const
{
return mProperties.isNotSet(CL_QUEUE_ON_DEVICE);
return mProperties->isNotSet(CL_QUEUE_ON_DEVICE);
}
inline bool CommandQueue::isOnDevice() const
{
return mProperties.isSet(CL_QUEUE_ON_DEVICE);
return mProperties->isSet(CL_QUEUE_ON_DEVICE);
}
inline bool CommandQueue::hasSize() const
......
......@@ -347,6 +347,8 @@ cl_int Device::createSubDevices(const cl_device_partition_property *properties,
while (!subDeviceCreateFuncs.empty())
{
devices.emplace_back(new Device(mPlatform, this, type, subDeviceCreateFuncs.front()));
// Release initialization reference, lifetime controlled by RefPointer.
devices.back()->release();
if (!devices.back()->mInfo.isValid())
{
return CL_INVALID_VALUE;
......
......@@ -12,6 +12,9 @@
#include "libANGLE/CLObject.h"
#include "libANGLE/renderer/CLDeviceImpl.h"
#include "common/Spinlock.h"
#include "common/SynchronizedValue.h"
#include <functional>
namespace cl
......@@ -59,7 +62,7 @@ class Device final : public _cl_device_id, public Object
const rx::CLDeviceImpl::Ptr mImpl;
const rx::CLDeviceImpl::Info mInfo;
CommandQueue *mDefaultCommandQueue = nullptr;
angle::SynchronizedValue<CommandQueue *, angle::Spinlock> mDefaultCommandQueue = nullptr;
friend class CommandQueue;
friend class Platform;
......
......@@ -91,18 +91,15 @@ cl_int Event::getInfo(EventInfo name, size_t valueSize, void *value, size_t *val
cl_int Event::setCallback(cl_int commandExecCallbackType, EventCB pfnNotify, void *userData)
{
auto callbacks = mCallbacks.synchronize();
// Only when required register a single callback with the back end for each callback type.
if (mCallbacks[commandExecCallbackType].empty())
if ((*callbacks)[commandExecCallbackType].empty())
{
const cl_int errorCode = mImpl->setCallback(*this, commandExecCallbackType);
if (errorCode != CL_SUCCESS)
{
return errorCode;
}
ANGLE_CL_TRY(mImpl->setCallback(*this, commandExecCallbackType));
// This event has to be retained until the callback is called.
retain();
}
mCallbacks[commandExecCallbackType].emplace_back(pfnNotify, userData);
(*callbacks)[commandExecCallbackType].emplace_back(pfnNotify, userData);
return CL_SUCCESS;
}
......@@ -119,7 +116,8 @@ Event::~Event() = default;
void Event::callback(cl_int commandStatus)
{
ASSERT(commandStatus >= 0 && commandStatus < 3);
for (const CallbackData &data : mCallbacks[commandStatus])
const Callbacks callbacks = std::move(mCallbacks->at(commandStatus));
for (const CallbackData &data : callbacks)
{
data.first(this, commandStatus, data.second);
}
......
......@@ -12,6 +12,8 @@
#include "libANGLE/CLObject.h"
#include "libANGLE/renderer/CLEventImpl.h"
#include "common/SynchronizedValue.h"
#include <array>
namespace cl
......@@ -51,6 +53,7 @@ class Event final : public _cl_event, public Object
private:
using CallbackData = std::pair<EventCB, void *>;
using Callbacks = std::vector<CallbackData>;
Event(Context &context, cl_int &errorCode);
......@@ -69,7 +72,7 @@ class Event final : public _cl_event, public Object
// Create separate storage for each possible callback type.
static_assert(CL_COMPLETE == 0 && CL_RUNNING == 1 && CL_SUBMITTED == 2,
"OpenCL command execution status values are not as assumed");
std::array<std::vector<CallbackData>, 3u> mCallbacks;
angle::SynchronizedValue<std::array<Callbacks, 3u>> mCallbacks;
friend class Object;
};
......
......@@ -45,7 +45,7 @@ MemFlags InheritMemFlags(MemFlags flags, Memory *parent)
cl_int Memory::setDestructorCallback(MemoryCB pfnNotify, void *userData)
{
mDestructorCallbacks.emplace(pfnNotify, userData);
mDestructorCallbacks->emplace(pfnNotify, userData);
return CL_SUCCESS;
}
......@@ -80,8 +80,9 @@ cl_int Memory::getInfo(MemInfo name, size_t valueSize, void *value, size_t *valu
copySize = sizeof(mHostPtr);
break;
case MemInfo::MapCount:
copyValue = &mMapCount;
copySize = sizeof(mMapCount);
valUInt = mMapCount;
copyValue = &valUInt;
copySize = sizeof(valUInt);
break;
case MemInfo::ReferenceCount:
valUInt = getRefCount();
......@@ -137,11 +138,13 @@ cl_int Memory::getInfo(MemInfo name, size_t valueSize, void *value, size_t *valu
Memory::~Memory()
{
while (!mDestructorCallbacks.empty())
std::stack<CallbackData> callbacks;
mDestructorCallbacks->swap(callbacks);
while (!callbacks.empty())
{
const MemoryCB callback = mDestructorCallbacks.top().first;
void *const userData = mDestructorCallbacks.top().second;
mDestructorCallbacks.pop();
const MemoryCB callback = callbacks.top().first;
void *const userData = callbacks.top().second;
callbacks.pop();
callback(this, userData);
}
}
......@@ -158,7 +161,8 @@ Memory::Memory(const Buffer &buffer,
mFlags(flags),
mHostPtr(flags.isSet(CL_MEM_USE_HOST_PTR) ? hostPtr : nullptr),
mImpl(context.getImpl().createBuffer(buffer, size, hostPtr, errorCode)),
mSize(size)
mSize(size),
mMapCount(0u)
{}
Memory::Memory(const Buffer &buffer,
......@@ -174,7 +178,8 @@ Memory::Memory(const Buffer &buffer,
mParent(&parent),
mOffset(offset),
mImpl(parent.mImpl->createSubBuffer(buffer, flags, size, errorCode)),
mSize(size)
mSize(size),
mMapCount(0u)
{}
Memory::Memory(const Image &image,
......@@ -192,7 +197,8 @@ Memory::Memory(const Image &image,
mHostPtr(flags.isSet(CL_MEM_USE_HOST_PTR) ? hostPtr : nullptr),
mParent(parent),
mImpl(context.getImpl().createImage(image, flags, format, desc, hostPtr, errorCode)),
mSize(mImpl ? mImpl->getSize(errorCode) : 0u)
mSize(mImpl ? mImpl->getSize(errorCode) : 0u),
mMapCount(0u)
{}
} // namespace cl
......@@ -12,6 +12,10 @@
#include "libANGLE/CLObject.h"
#include "libANGLE/renderer/CLMemoryImpl.h"
#include "common/Spinlock.h"
#include "common/SynchronizedValue.h"
#include <atomic>
#include <stack>
namespace cl
......@@ -83,8 +87,8 @@ class Memory : public _cl_mem, public Object
const rx::CLMemoryImpl::Ptr mImpl;
const size_t mSize;
std::stack<CallbackData> mDestructorCallbacks;
cl_uint mMapCount = 0u;
angle::SynchronizedValue<std::stack<CallbackData>, angle::Spinlock> mDestructorCallbacks;
std::atomic<cl_uint> mMapCount;
friend class Buffer;
friend class Context;
......
......@@ -10,7 +10,7 @@
namespace cl
{
Object::Object() : mRefCount(0u) {}
Object::Object() : mRefCount(1u) {}
Object::~Object()
{
......
......@@ -39,8 +39,13 @@ class Object
template <typename T, typename... Args>
static T *Create(cl_int &errorCode, Args &&... args)
{
RefPointer<T> object(new T(std::forward<Args>(args)..., errorCode));
return errorCode == CL_SUCCESS ? object.release() : nullptr;
T *object = new T(std::forward<Args>(args)..., errorCode);
if (errorCode != CL_SUCCESS)
{
delete object;
object = nullptr;
}
return object;
}
private:
......
......@@ -76,6 +76,8 @@ void Platform::Initialize(const cl_icd_dispatch &dispatch,
while (!createFuncs.empty())
{
platforms.emplace_back(new Platform(createFuncs.front()));
// Release initialization reference, lifetime controlled by RefPointer.
platforms.back()->release();
if (!platforms.back()->mInfo.isValid() || platforms.back()->mDevices.empty())
{
platforms.pop_back();
......@@ -264,6 +266,8 @@ DevicePtrs Platform::createDevices(rx::CLDeviceImpl::CreateDatas &&createDatas)
{
devices.emplace_back(
new Device(*this, nullptr, createDatas.front().first, createDatas.front().second));
// Release initialization reference, lifetime controlled by RefPointer.
devices.back()->release();
if (!devices.back()->mInfo.isValid())
{
devices.pop_back();
......
......@@ -32,9 +32,8 @@ cl_int Program::build(cl_uint numDevices,
{
// This program has to be retained until the notify callback is called.
retain();
mCallback = pfnNotify;
mUserData = userData;
notify = this;
*mCallback = CallbackData(pfnNotify, userData);
notify = this;
}
return mImpl->build(devices, options, notify);
}
......@@ -65,9 +64,8 @@ cl_int Program::compile(cl_uint numDevices,
{
// This program has to be retained until the notify callback is called.
retain();
mCallback = pfnNotify;
mUserData = userData;
notify = this;
*mCallback = CallbackData(pfnNotify, userData);
notify = this;
}
return mImpl->compile(devices, options, programs, headerIncludeNames, notify);
}
......@@ -192,11 +190,11 @@ Program::~Program() = default;
void Program::callback()
{
ASSERT(mCallback != nullptr);
const ProgramCB callback = mCallback;
void *const userData = mUserData;
mCallback = nullptr;
mUserData = nullptr;
CallbackData callbackData;
mCallback->swap(callbackData);
const ProgramCB callback = callbackData.first;
void *const userData = callbackData.second;
ASSERT(callback != nullptr);
callback(this, userData);
// This program can be released after the callback was called.
if (release())
......@@ -208,18 +206,18 @@ void Program::callback()
Program::Program(Context &context, std::string &&source, cl_int &errorCode)
: mContext(&context),
mDevices(context.getDevices()),
mNumAttachedKernels(0u),
mImpl(context.getImpl().createProgramWithSource(*this, source, errorCode)),
mSource(std::move(source)),
mNumAttachedKernels(0u)
mSource(std::move(source))
{}
Program::Program(Context &context, const void *il, size_t length, cl_int &errorCode)
: mContext(&context),
mDevices(context.getDevices()),
mIL(static_cast<const char *>(il), length),
mNumAttachedKernels(0u),
mImpl(context.getImpl().createProgramWithIL(*this, il, length, errorCode)),
mSource(mImpl ? mImpl->getSource(errorCode) : std::string{}),
mNumAttachedKernels(0u)
mSource(mImpl ? mImpl->getSource(errorCode) : std::string{})
{}
Program::Program(Context &context,
......@@ -230,18 +228,18 @@ Program::Program(Context &context,
cl_int &errorCode)
: mContext(&context),
mDevices(std::move(devices)),
mNumAttachedKernels(0u),
mImpl(context.getImpl()
.createProgramWithBinary(*this, lengths, binaries, binaryStatus, errorCode)),
mSource(mImpl ? mImpl->getSource(errorCode) : std::string{}),
mNumAttachedKernels(0u)
mSource(mImpl ? mImpl->getSource(errorCode) : std::string{})
{}
Program::Program(Context &context, DevicePtrs &&devices, const char *kernelNames, cl_int &errorCode)
: mContext(&context),
mDevices(std::move(devices)),
mNumAttachedKernels(0u),
mImpl(context.getImpl().createProgramWithBuiltInKernels(*this, kernelNames, errorCode)),
mSource(mImpl ? mImpl->getSource(errorCode) : std::string{}),
mNumAttachedKernels(0u)
mSource(mImpl ? mImpl->getSource(errorCode) : std::string{})
{}
Program::Program(Context &context,
......@@ -253,22 +251,17 @@ Program::Program(Context &context,
cl_int &errorCode)
: mContext(&context),
mDevices(!devices.empty() ? devices : context.getDevices()),
// This program has to be retained until the notify callback is called.
mCallback(pfnNotify != nullptr ? (retain(), CallbackData(pfnNotify, userData))
: CallbackData()),
mNumAttachedKernels(0u),
mImpl(context.getImpl().linkProgram(*this,
devices,
options,
inputPrograms,
pfnNotify != nullptr ? this : nullptr,
errorCode)),
mSource(mImpl ? mImpl->getSource(errorCode) : std::string{}),
mCallback(pfnNotify),
mUserData(userData),
mNumAttachedKernels(0u)
{
if (mCallback != nullptr)
{
// This program has to be retained until the notify callback is called.
retain();
}
}
mSource(mImpl ? mImpl->getSource(errorCode) : std::string{})
{}
} // namespace cl
......@@ -12,6 +12,9 @@
#include "libANGLE/CLKernel.h"
#include "libANGLE/renderer/CLProgramImpl.h"
#include "common/Spinlock.h"
#include "common/SynchronizedValue.h"
#include <atomic>
namespace cl
......@@ -86,17 +89,20 @@ class Program final : public _cl_program, public Object
void *userData,
cl_int &errorCode);
using CallbackData = std::pair<ProgramCB, void *>;
const ContextPtr mContext;
const DevicePtrs mDevices;
const std::string mIL;
const rx::CLProgramImpl::Ptr mImpl;
const std::string mSource;
ProgramCB mCallback = nullptr;
void *mUserData = nullptr;
// mCallback might be accessed from implementation initialization
// and needs to be initialized first.
angle::SynchronizedValue<CallbackData, angle::Spinlock> mCallback;
std::atomic<cl_uint> mNumAttachedKernels;
const rx::CLProgramImpl::Ptr mImpl;
const std::string mSource;
friend class Kernel;
friend class Object;
};
......@@ -123,7 +129,7 @@ inline bool Program::hasDevice(const _cl_device_id *device) const
inline bool Program::isBuilding() const
{
return mCallback != nullptr;
return mCallback->first != nullptr;
}
inline bool Program::hasAttachedKernels() const
......
......@@ -42,7 +42,7 @@ CLCommandQueueCL::CLCommandQueueCL(const cl::CommandQueue &commandQueue, cl_comm
{
if (commandQueue.getProperties().isSet(CL_QUEUE_ON_DEVICE))
{
commandQueue.getContext().getImpl<CLContextCL>().mDeviceQueues.emplace(
commandQueue.getContext().getImpl<CLContextCL>().mData->mDeviceQueues.emplace(
commandQueue.getNative());
}
}
......@@ -52,7 +52,7 @@ CLCommandQueueCL::~CLCommandQueueCL()
if (mCommandQueue.getProperties().isSet(CL_QUEUE_ON_DEVICE))
{
const size_t numRemoved =
mCommandQueue.getContext().getImpl<CLContextCL>().mDeviceQueues.erase(
mCommandQueue.getContext().getImpl<CLContextCL>().mData->mDeviceQueues.erase(
mCommandQueue.getNative());
ASSERT(numRemoved == 1u);
}
......
......@@ -12,6 +12,9 @@
#include "libANGLE/renderer/CLContextImpl.h"
#include "common/Spinlock.h"
#include "common/SynchronizedValue.h"
#include <unordered_set>
namespace rx
......@@ -83,11 +86,16 @@ class CLContextCL : public CLContextImpl
cl_int waitForEvents(const cl::EventPtrs &events) override;
private:
const cl_context mNative;
struct Mutable
{
std::unordered_set<const _cl_mem *> mMemories;
std::unordered_set<const _cl_sampler *> mSamplers;
std::unordered_set<const _cl_command_queue *> mDeviceQueues;
};
using MutableData = angle::SynchronizedValue<Mutable, angle::Spinlock>;
std::unordered_set<const _cl_mem *> mMemories;
std::unordered_set<const _cl_sampler *> mSamplers;
std::unordered_set<const _cl_command_queue *> mDeviceQueues;
const cl_context mNative;
MutableData mData;
friend class CLCommandQueueCL;
friend class CLMemoryCL;
......@@ -96,17 +104,20 @@ class CLContextCL : public CLContextImpl
inline bool CLContextCL::hasMemory(cl_mem memory) const
{
return mMemories.find(memory) != mMemories.cend();
const auto data = mData.synchronize();
return data->mMemories.find(memory) != data->mMemories.cend();
}
inline bool CLContextCL::hasSampler(cl_sampler sampler) const
{
return mSamplers.find(sampler) != mSamplers.cend();
const auto data = mData.synchronize();
return data->mSamplers.find(sampler) != data->mSamplers.cend();
}
inline bool CLContextCL::hasDeviceQueue(cl_command_queue queue) const
{
return mDeviceQueues.find(queue) != mDeviceQueues.cend();
const auto data = mData.synchronize();
return data->mDeviceQueues.find(queue) != data->mDeviceQueues.cend();
}
} // namespace rx
......
......@@ -18,13 +18,13 @@ namespace rx
CLMemoryCL::CLMemoryCL(const cl::Memory &memory, cl_mem native)
: CLMemoryImpl(memory), mNative(native)
{
memory.getContext().getImpl<CLContextCL>().mMemories.emplace(memory.getNative());
memory.getContext().getImpl<CLContextCL>().mData->mMemories.emplace(memory.getNative());
}
CLMemoryCL::~CLMemoryCL()
{
const size_t numRemoved =
mMemory.getContext().getImpl<CLContextCL>().mMemories.erase(mMemory.getNative());
mMemory.getContext().getImpl<CLContextCL>().mData->mMemories.erase(mMemory.getNative());
ASSERT(numRemoved == 1u);
if (mNative->getDispatch().clReleaseMemObject(mNative) != CL_SUCCESS)
......
......@@ -18,13 +18,13 @@ namespace rx
CLSamplerCL::CLSamplerCL(const cl::Sampler &sampler, cl_sampler native)
: CLSamplerImpl(sampler), mNative(native)
{
sampler.getContext().getImpl<CLContextCL>().mSamplers.emplace(sampler.getNative());
sampler.getContext().getImpl<CLContextCL>().mData->mSamplers.emplace(sampler.getNative());
}
CLSamplerCL::~CLSamplerCL()
{
const size_t numRemoved =
mSampler.getContext().getImpl<CLContextCL>().mSamplers.erase(mSampler.getNative());
mSampler.getContext().getImpl<CLContextCL>().mData->mSamplers.erase(mSampler.getNative());
ASSERT(numRemoved == 1u);
if (mNative->getDispatch().clReleaseSampler(mNative) != CL_SUCCESS)
......
......@@ -19,6 +19,8 @@ libangle_common_sources = [
"src/common/PackedGLEnums_autogen.h",
"src/common/PoolAlloc.cpp",
"src/common/PoolAlloc.h",
"src/common/Spinlock.h",
"src/common/SynchronizedValue.h",
"src/common/aligned_memory.cpp",
"src/common/aligned_memory.h",
"src/common/android_util.cpp",
......
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