Commit 1e5499db by Jamie Madill Committed by Commit Bot

Refactor Signal utils into template classes.

This will allow us to pass on extra information to the receiving end, such as the specific texture levels that are dirty. BUG=angleproject:1635 Change-Id: Idb7ca1d625499e50e7712c458b694f6e9bfc0595 Reviewed-on: https://chromium-review.googlesource.com/453382 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 2b39659d
......@@ -34,7 +34,7 @@ namespace gl
namespace
{
void BindResourceChannel(ChannelBinding *binding, FramebufferAttachmentObject *resource)
void BindResourceChannel(OnAttachmentDirtyBinding *binding, FramebufferAttachmentObject *resource)
{
binding->bind(resource ? resource->getDirtyChannel() : nullptr);
}
......@@ -298,10 +298,10 @@ Framebuffer::Framebuffer(const Caps &caps, rx::GLImplFactory *factory, GLuint id
ASSERT(mImpl != nullptr);
ASSERT(mState.mColorAttachments.size() == static_cast<size_t>(caps.maxColorAttachments));
for (size_t colorIndex = 0; colorIndex < mState.mColorAttachments.size(); ++colorIndex)
for (uint32_t colorIndex = 0;
colorIndex < static_cast<uint32_t>(mState.mColorAttachments.size()); ++colorIndex)
{
mDirtyColorAttachmentBindings.push_back(ChannelBinding(
this, static_cast<SignalToken>(DIRTY_BIT_COLOR_ATTACHMENT_0 + colorIndex)));
mDirtyColorAttachmentBindings.emplace_back(this, DIRTY_BIT_COLOR_ATTACHMENT_0 + colorIndex);
}
}
......@@ -314,8 +314,7 @@ Framebuffer::Framebuffer(egl::Surface *surface)
mDirtyStencilAttachmentBinding(this, DIRTY_BIT_STENCIL_ATTACHMENT)
{
ASSERT(mImpl != nullptr);
mDirtyColorAttachmentBindings.push_back(
ChannelBinding(this, static_cast<SignalToken>(DIRTY_BIT_COLOR_ATTACHMENT_0)));
mDirtyColorAttachmentBindings.emplace_back(this, DIRTY_BIT_COLOR_ATTACHMENT_0);
setAttachmentImpl(GL_FRAMEBUFFER_DEFAULT, GL_BACK, gl::ImageIndex::MakeInvalid(), surface);
......@@ -339,8 +338,7 @@ Framebuffer::Framebuffer(rx::GLImplFactory *factory)
mDirtyDepthAttachmentBinding(this, DIRTY_BIT_DEPTH_ATTACHMENT),
mDirtyStencilAttachmentBinding(this, DIRTY_BIT_STENCIL_ATTACHMENT)
{
mDirtyColorAttachmentBindings.push_back(
ChannelBinding(this, static_cast<SignalToken>(DIRTY_BIT_COLOR_ATTACHMENT_0)));
mDirtyColorAttachmentBindings.emplace_back(this, DIRTY_BIT_COLOR_ATTACHMENT_0);
}
Framebuffer::~Framebuffer()
......@@ -1220,7 +1218,7 @@ void Framebuffer::syncState(const Context *context)
}
}
void Framebuffer::signal(SignalToken token)
void Framebuffer::signal(uint32_t token)
{
// TOOD(jmadill): Make this only update individual attachments to do less work.
mCachedStatus.reset();
......
......@@ -115,7 +115,10 @@ class FramebufferState final : angle::NonCopyable
bool mWebGLDepthStencilConsistent;
};
class Framebuffer final : public LabeledObject, public angle::SignalReceiver
using OnAttachmentDirtyReceiver = angle::SignalReceiver<>;
using OnAttachmentDirtyBinding = angle::ChannelBinding<>;
class Framebuffer final : public LabeledObject, public OnAttachmentDirtyReceiver
{
public:
// Constructor to build application-defined framebuffers
......@@ -239,7 +242,7 @@ class Framebuffer final : public LabeledObject, public angle::SignalReceiver
GLbitfield mask,
GLenum filter);
enum DirtyBitType
enum DirtyBitType : uint32_t
{
DIRTY_BIT_COLOR_ATTACHMENT_0,
DIRTY_BIT_COLOR_ATTACHMENT_MAX =
......@@ -262,7 +265,7 @@ class Framebuffer final : public LabeledObject, public angle::SignalReceiver
void syncState(const Context *context);
// angle::SignalReceiver implementation
void signal(angle::SignalToken token) override;
void signal(uint32_t token) override;
bool formsRenderingFeedbackLoopWith(const State &state) const;
bool formsCopyingFeedbackLoopWith(GLuint copyTextureID,
......@@ -288,9 +291,9 @@ class Framebuffer final : public LabeledObject, public angle::SignalReceiver
GLuint mId;
Optional<GLenum> mCachedStatus;
std::vector<angle::ChannelBinding> mDirtyColorAttachmentBindings;
angle::ChannelBinding mDirtyDepthAttachmentBinding;
angle::ChannelBinding mDirtyStencilAttachmentBinding;
std::vector<OnAttachmentDirtyBinding> mDirtyColorAttachmentBindings;
OnAttachmentDirtyBinding mDirtyDepthAttachmentBinding;
OnAttachmentDirtyBinding mDirtyStencilAttachmentBinding;
DirtyBits mDirtyBits;
};
......
......@@ -242,7 +242,7 @@ Error FramebufferAttachmentObject::getAttachmentRenderTarget(
return getAttachmentImpl()->getAttachmentRenderTarget(target, rtOut);
}
angle::BroadcastChannel *FramebufferAttachmentObject::getDirtyChannel()
angle::BroadcastChannel<> *FramebufferAttachmentObject::getDirtyChannel()
{
return &mDirtyChannel;
}
......
......@@ -168,12 +168,12 @@ class FramebufferAttachmentObject
Error getAttachmentRenderTarget(const FramebufferAttachment::Target &target,
rx::FramebufferAttachmentRenderTarget **rtOut) const;
angle::BroadcastChannel *getDirtyChannel();
angle::BroadcastChannel<> *getDirtyChannel();
protected:
virtual rx::FramebufferAttachmentObjectImpl *getAttachmentImpl() const = 0;
angle::BroadcastChannel mDirtyChannel;
angle::BroadcastChannel<> mDirtyChannel;
};
inline Extents FramebufferAttachment::getSize() const
......
......@@ -143,7 +143,7 @@ class Buffer11::NativeStorage : public Buffer11::BufferStorage
public:
NativeStorage(Renderer11 *renderer,
BufferUsage usage,
const angle::BroadcastChannel *onStorageChanged);
const angle::BroadcastChannel<> *onStorageChanged);
~NativeStorage() override;
bool isMappable(GLbitfield access) const override;
......@@ -171,7 +171,7 @@ class Buffer11::NativeStorage : public Buffer11::BufferStorage
void clearSRVs();
ID3D11Buffer *mNativeStorage;
const angle::BroadcastChannel *mOnStorageChanged;
const angle::BroadcastChannel<> *mOnStorageChanged;
std::map<DXGI_FORMAT, ID3D11ShaderResourceView *> mBufferResourceViews;
};
......@@ -902,12 +902,12 @@ void Buffer11::invalidateStaticData()
mStaticBroadcastChannel.signal();
}
angle::BroadcastChannel *Buffer11::getStaticBroadcastChannel()
angle::BroadcastChannel<> *Buffer11::getStaticBroadcastChannel()
{
return &mStaticBroadcastChannel;
}
angle::BroadcastChannel *Buffer11::getDirectBroadcastChannel()
angle::BroadcastChannel<> *Buffer11::getDirectBroadcastChannel()
{
return &mDirectBroadcastChannel;
}
......@@ -941,7 +941,7 @@ gl::Error Buffer11::BufferStorage::setData(const uint8_t *data, size_t offset, s
Buffer11::NativeStorage::NativeStorage(Renderer11 *renderer,
BufferUsage usage,
const angle::BroadcastChannel *onStorageChanged)
const angle::BroadcastChannel<> *onStorageChanged)
: BufferStorage(renderer, usage), mNativeStorage(nullptr), mOnStorageChanged(onStorageChanged)
{
}
......
......@@ -103,8 +103,8 @@ class Buffer11 : public BufferD3D
// We use two set of dirty events. Static buffers are marked dirty whenever
// data changes, because they must be re-translated. Direct buffers only need to be
// updated when the underlying ID3D11Buffer pointer changes - hopefully far less often.
angle::BroadcastChannel *getStaticBroadcastChannel();
angle::BroadcastChannel *getDirectBroadcastChannel();
angle::BroadcastChannel<> *getStaticBroadcastChannel();
angle::BroadcastChannel<> *getDirectBroadcastChannel();
private:
class BufferStorage;
......@@ -162,8 +162,8 @@ class Buffer11 : public BufferD3D
size_t mConstantBufferStorageAdditionalSize;
unsigned int mMaxConstantBufferLruCount;
angle::BroadcastChannel mStaticBroadcastChannel;
angle::BroadcastChannel mDirectBroadcastChannel;
angle::BroadcastChannel<> mStaticBroadcastChannel;
angle::BroadcastChannel<> mDirectBroadcastChannel;
};
} // namespace rx
......
......@@ -54,7 +54,7 @@ gl::Error MarkAttachmentsDirty(const gl::FramebufferAttachment *attachment)
void UpdateCachedRenderTarget(const gl::FramebufferAttachment *attachment,
RenderTarget11 *&cachedRenderTarget,
ChannelBinding *channelBinding)
gl::OnAttachmentDirtyBinding *channelBinding)
{
RenderTarget11 *newRenderTarget = nullptr;
if (attachment)
......@@ -78,10 +78,10 @@ Framebuffer11::Framebuffer11(const gl::FramebufferState &data, Renderer11 *rende
{
ASSERT(mRenderer != nullptr);
mCachedColorRenderTargets.fill(nullptr);
for (size_t colorIndex = 0; colorIndex < data.getColorAttachments().size(); ++colorIndex)
for (uint32_t colorIndex = 0;
colorIndex < static_cast<uint32_t>(data.getColorAttachments().size()); ++colorIndex)
{
mColorRenderTargetsDirty.push_back(
ChannelBinding(this, static_cast<SignalToken>(colorIndex)));
mColorRenderTargetsDirty.emplace_back(this, colorIndex);
}
}
......@@ -413,9 +413,9 @@ void Framebuffer11::syncState(ContextImpl *contextImpl, const gl::Framebuffer::D
FramebufferD3D::syncState(contextImpl, dirtyBits);
}
void Framebuffer11::signal(SignalToken token)
void Framebuffer11::signal(uint32_t channelID)
{
if (token == gl::IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS)
if (channelID == gl::IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS)
{
// Stencil is redundant in this case.
mInternalDirtyBits.set(gl::Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT);
......@@ -423,8 +423,8 @@ void Framebuffer11::signal(SignalToken token)
}
else
{
mInternalDirtyBits.set(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 + token);
mCachedColorRenderTargets[token] = nullptr;
mInternalDirtyBits.set(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 + channelID);
mCachedColorRenderTargets[channelID] = nullptr;
}
}
......
......@@ -17,7 +17,7 @@ namespace rx
{
class Renderer11;
class Framebuffer11 : public FramebufferD3D, public angle::SignalReceiver
class Framebuffer11 : public FramebufferD3D, public angle::SignalReceiver<>
{
public:
Framebuffer11(const gl::FramebufferState &data, Renderer11 *renderer);
......@@ -44,7 +44,7 @@ class Framebuffer11 : public FramebufferD3D, public angle::SignalReceiver
bool hasAnyInternalDirtyBit() const;
void syncInternalState(ContextImpl *contextImpl);
void signal(angle::SignalToken token) override;
void signal(uint32_t channelID) override;
gl::Error getSamplePosition(size_t index, GLfloat *xy) const override;
......@@ -79,8 +79,8 @@ class Framebuffer11 : public FramebufferD3D, public angle::SignalReceiver
RenderTargetArray mCachedColorRenderTargets;
RenderTarget11 *mCachedDepthStencilRenderTarget;
std::vector<angle::ChannelBinding> mColorRenderTargetsDirty;
angle::ChannelBinding mDepthStencilRenderTargetDirty;
std::vector<gl::OnAttachmentDirtyBinding> mColorRenderTargetsDirty;
gl::OnAttachmentDirtyBinding mDepthStencilRenderTargetDirty;
gl::Framebuffer::DirtyBits mInternalDirtyBits;
};
......
......@@ -35,12 +35,12 @@ class RenderTarget11 : public RenderTargetD3D
virtual unsigned int getSubresourceIndex() const = 0;
void signalDirty() override;
angle::BroadcastChannel *getBroadcastChannel() { return &mBroadcastChannel; }
angle::BroadcastChannel<> *getBroadcastChannel() { return &mBroadcastChannel; }
const d3d11::Format &getFormatSet() const { return mFormatSet; }
protected:
angle::BroadcastChannel mBroadcastChannel;
angle::BroadcastChannel<> mBroadcastChannel;
const d3d11::Format &mFormatSet;
};
......
......@@ -25,7 +25,7 @@ VertexArray11::VertexArray11(const gl::VertexArrayState &data)
{
for (size_t attribIndex = 0; attribIndex < mCurrentBuffers.size(); ++attribIndex)
{
mOnBufferDataDirty.emplace_back(this, static_cast<SignalToken>(attribIndex));
mOnBufferDataDirty.emplace_back(this, static_cast<uint32_t>(attribIndex));
}
}
......@@ -112,7 +112,7 @@ void VertexArray11::updateVertexAttribStorage(size_t attribIndex)
{
// Note that for static callbacks, promotion to a static buffer from a dynamic buffer means
// we need to tag dynamic buffers with static callbacks.
BroadcastChannel *newChannel = nullptr;
BroadcastChannel<> *newChannel = nullptr;
if (newBuffer11 != nullptr)
{
switch (newStorageType)
......@@ -220,12 +220,12 @@ const std::vector<TranslatedAttribute> &VertexArray11::getTranslatedAttribs() co
return mTranslatedAttribs;
}
void VertexArray11::signal(SignalToken token)
void VertexArray11::signal(uint32_t channelID)
{
ASSERT(mAttributeStorageTypes[token] != VertexStorageType::CURRENT_VALUE);
ASSERT(mAttributeStorageTypes[channelID] != VertexStorageType::CURRENT_VALUE);
// This can change a buffer's storage, we'll need to re-check.
mAttribsToUpdate.set(token);
mAttribsToUpdate.set(channelID);
}
void VertexArray11::clearDirtyAndPromoteDynamicAttribs(const gl::State &state, GLsizei count)
......
......@@ -9,6 +9,7 @@
#ifndef LIBANGLE_RENDERER_D3D_D3D11_VERTEXARRAY11_H_
#define LIBANGLE_RENDERER_D3D_D3D11_VERTEXARRAY11_H_
#include "libANGLE/Framebuffer.h"
#include "libANGLE/renderer/VertexArrayImpl.h"
#include "libANGLE/renderer/d3d/d3d11/Renderer11.h"
#include "libANGLE/signal_utils.h"
......@@ -17,7 +18,7 @@ namespace rx
{
class Renderer11;
class VertexArray11 : public VertexArrayImpl, public angle::SignalReceiver
class VertexArray11 : public VertexArrayImpl, public angle::SignalReceiver<>
{
public:
VertexArray11(const gl::VertexArrayState &data);
......@@ -36,7 +37,7 @@ class VertexArray11 : public VertexArrayImpl, public angle::SignalReceiver
const std::vector<TranslatedAttribute> &getTranslatedAttribs() const;
// SignalReceiver implementation
void signal(angle::SignalToken token) override;
void signal(uint32_t channelID) override;
private:
void updateVertexAttribStorage(size_t attribIndex);
......@@ -57,7 +58,7 @@ class VertexArray11 : public VertexArrayImpl, public angle::SignalReceiver
// We need to keep a safe pointer to the Buffer so we can attach the correct dirty callbacks.
std::vector<BindingPointer<gl::Buffer>> mCurrentBuffers;
std::vector<angle::ChannelBinding> mOnBufferDataDirty;
std::vector<gl::OnAttachmentDirtyBinding> mOnBufferDataDirty;
};
} // namespace rx
......
//
// Copyright 2016 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.
//
// signal_utils:
// Helper classes for tracking dependent state changes between objects.
// These changes are signaled to the dependent class via channels.
#include "libANGLE/signal_utils.h"
#include <algorithm>
#include "common/debug.h"
namespace angle
{
BroadcastChannel::BroadcastChannel()
{
}
BroadcastChannel::~BroadcastChannel()
{
reset();
}
void BroadcastChannel::addReceiver(ChannelBinding *receiver)
{
ASSERT(std::find(mReceivers.begin(), mReceivers.end(), receiver) == mReceivers.end());
mReceivers.push_back(receiver);
}
void BroadcastChannel::removeReceiver(ChannelBinding *receiver)
{
auto iter = std::find(mReceivers.begin(), mReceivers.end(), receiver);
ASSERT(iter != mReceivers.end());
mReceivers.erase(iter);
}
void BroadcastChannel::signal() const
{
if (mReceivers.empty())
return;
for (const auto *receiver : mReceivers)
{
receiver->signal();
}
}
void BroadcastChannel::reset()
{
for (auto receiver : mReceivers)
{
receiver->onChannelClosed();
}
mReceivers.clear();
}
ChannelBinding::ChannelBinding(SignalReceiver *receiver, SignalToken token)
: mChannel(nullptr), mReceiver(receiver), mToken(token)
{
ASSERT(receiver);
}
ChannelBinding::~ChannelBinding()
{
reset();
}
void ChannelBinding::bind(BroadcastChannel *channel)
{
ASSERT(mReceiver);
if (mChannel)
{
mChannel->removeReceiver(this);
}
mChannel = channel;
if (mChannel)
{
mChannel->addReceiver(this);
}
}
void ChannelBinding::reset()
{
bind(nullptr);
}
void ChannelBinding::signal() const
{
mReceiver->signal(mToken);
}
void ChannelBinding::onChannelClosed()
{
mChannel = nullptr;
}
} // namespace angle
......@@ -13,62 +13,165 @@
#include <set>
#include "common/angleutils.h"
#include "common/debug.h"
namespace angle
{
// Message token passed to the receiver;
using SignalToken = uint32_t;
// Interface that the depending class inherits from.
template <typename ChannelID = uint32_t, typename... MessageT>
class SignalReceiver
{
public:
virtual ~SignalReceiver() = default;
virtual void signal(SignalToken token) = 0;
virtual void signal(ChannelID channelID, MessageT... message) = 0;
};
template <typename ChannelID, typename... MessageT>
class ChannelBinding;
// The host class owns the channel. It uses the channel to fire signals to the receiver.
template <typename ChannelID = uint32_t, typename... MessageT>
class BroadcastChannel final : NonCopyable
{
public:
BroadcastChannel();
~BroadcastChannel();
void signal() const;
void signal(MessageT... message) const;
void reset();
private:
// Only the ChannelBinding class should add or remove receivers.
friend class ChannelBinding;
void addReceiver(ChannelBinding *receiver);
void removeReceiver(ChannelBinding *receiver);
friend class ChannelBinding<ChannelID, MessageT...>;
void addReceiver(ChannelBinding<ChannelID, MessageT...> *receiver);
void removeReceiver(ChannelBinding<ChannelID, MessageT...> *receiver);
std::vector<ChannelBinding *> mReceivers;
std::vector<ChannelBinding<ChannelID, MessageT...> *> mReceivers;
};
template <typename ChannelID, typename... MessageT>
BroadcastChannel<ChannelID, MessageT...>::BroadcastChannel()
{
}
template <typename ChannelID, typename... MessageT>
BroadcastChannel<ChannelID, MessageT...>::~BroadcastChannel()
{
reset();
}
template <typename ChannelID, typename... MessageT>
void BroadcastChannel<ChannelID, MessageT...>::addReceiver(
ChannelBinding<ChannelID, MessageT...> *receiver)
{
ASSERT(std::find(mReceivers.begin(), mReceivers.end(), receiver) == mReceivers.end());
mReceivers.push_back(receiver);
}
template <typename ChannelID, typename... MessageT>
void BroadcastChannel<ChannelID, MessageT...>::removeReceiver(
ChannelBinding<ChannelID, MessageT...> *receiver)
{
auto iter = std::find(mReceivers.begin(), mReceivers.end(), receiver);
ASSERT(iter != mReceivers.end());
mReceivers.erase(iter);
}
template <typename ChannelID, typename... MessageT>
void BroadcastChannel<ChannelID, MessageT...>::signal(MessageT... message) const
{
if (mReceivers.empty())
return;
for (const auto *receiver : mReceivers)
{
receiver->signal(message...);
}
}
template <typename ChannelID, typename... MessageT>
void BroadcastChannel<ChannelID, MessageT...>::reset()
{
for (auto receiver : mReceivers)
{
receiver->onChannelClosed();
}
mReceivers.clear();
}
// The dependent class keeps bindings to the host's BroadcastChannel.
template <typename ChannelID = uint32_t, typename... MessageT>
class ChannelBinding final
{
public:
ChannelBinding(SignalReceiver *receiver, SignalToken token);
ChannelBinding(SignalReceiver<ChannelID, MessageT...> *receiver, ChannelID channelID);
~ChannelBinding();
ChannelBinding(const ChannelBinding &other) = default;
ChannelBinding &operator=(const ChannelBinding &other) = default;
void bind(BroadcastChannel *channel);
void bind(BroadcastChannel<ChannelID, MessageT...> *channel);
void reset();
void signal() const;
void signal(MessageT... message) const;
void onChannelClosed();
private:
BroadcastChannel *mChannel;
SignalReceiver *mReceiver;
SignalToken mToken;
BroadcastChannel<ChannelID, MessageT...> *mChannel;
SignalReceiver<ChannelID, MessageT...> *mReceiver;
ChannelID mChannelID;
};
template <typename ChannelID, typename... MessageT>
ChannelBinding<ChannelID, MessageT...>::ChannelBinding(
SignalReceiver<ChannelID, MessageT...> *receiver,
ChannelID channelID)
: mChannel(nullptr), mReceiver(receiver), mChannelID(channelID)
{
ASSERT(receiver);
}
template <typename ChannelID, typename... MessageT>
ChannelBinding<ChannelID, MessageT...>::~ChannelBinding()
{
reset();
}
template <typename ChannelID, typename... MessageT>
void ChannelBinding<ChannelID, MessageT...>::bind(BroadcastChannel<ChannelID, MessageT...> *channel)
{
ASSERT(mReceiver);
if (mChannel)
{
mChannel->removeReceiver(this);
}
mChannel = channel;
if (mChannel)
{
mChannel->addReceiver(this);
}
}
template <typename ChannelID, typename... MessageT>
void ChannelBinding<ChannelID, MessageT...>::reset()
{
bind(nullptr);
}
template <typename ChannelID, typename... MessageT>
void ChannelBinding<ChannelID, MessageT...>::signal(MessageT... message) const
{
mReceiver->signal(mChannelID, message...);
}
template <typename ChannelID, typename... MessageT>
void ChannelBinding<ChannelID, MessageT...>::onChannelClosed()
{
mChannel = nullptr;
}
} // namespace angle
#endif // LIBANGLE_SIGNAL_UTILS_H_
......@@ -16,18 +16,18 @@ using namespace testing;
namespace
{
struct SignalThing : public SignalReceiver
struct SignalThing : public SignalReceiver<>
{
void signal(SignalToken token) override { wasSignaled = true; }
void signal(uint32_t channelID) override { wasSignaled = true; }
bool wasSignaled = false;
};
// Test that broadcast signals work.
TEST(SignalTest, BroadcastSignals)
{
BroadcastChannel channel;
BroadcastChannel<> channel;
SignalThing thing;
ChannelBinding binding(&thing, 0u);
ChannelBinding<> binding(&thing, 0u);
binding.bind(&channel);
ASSERT_FALSE(thing.wasSignaled);
......
......@@ -248,7 +248,6 @@
'libANGLE/renderer/load_functions_table_autogen.cpp',
'libANGLE/renderer/renderer_utils.cpp',
'libANGLE/renderer/renderer_utils.h',
'libANGLE/signal_utils.cpp',
'libANGLE/signal_utils.h',
'libANGLE/validationEGL.cpp',
'libANGLE/validationEGL.h',
......
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