Commit 4c833efb by Tobin Ehlis Committed by Commit Bot

Vulkan: Enable sampler objects

This is the initial implementation to get GLES3.0 Sampler Objects working on the Vulkan backend. When samplers are dirty, a corresponding Vulkan sampler object will be created with associated state and textures are flagged as dirty anytime sampler bindings change. Then when handling textures dirty, any bound sampler objects are pulled from active texture units. When sampler objects are bound, their state is used instead of the built-in texture's sampler state. This change also adds a test that uses the same texture object with different sampler objects to test a corner case that dEQP misses. Bug: angleproject:3208 Change-Id: I643d9c9d5cb92fecc02dad815a07bcf349534c70 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1706897 Commit-Queue: Tobin Ehlis <tobine@google.com> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 23c0463b
...@@ -16,15 +16,21 @@ namespace gl ...@@ -16,15 +16,21 @@ namespace gl
{ {
Sampler::Sampler(rx::GLImplFactory *factory, GLuint id) Sampler::Sampler(rx::GLImplFactory *factory, GLuint id)
: RefCountObject(id), mState(), mImpl(factory->createSampler(mState)), mLabel() : RefCountObject(id), mState(), mDirty(true), mSampler(factory->createSampler(mState)), mLabel()
{} {}
Sampler::~Sampler() Sampler::~Sampler()
{ {
SafeDelete(mImpl); SafeDelete(mSampler);
} }
void Sampler::onDestroy(const Context *context) {} void Sampler::onDestroy(const Context *context)
{
if (mSampler)
{
mSampler->onDestroy(context);
}
}
void Sampler::setLabel(const Context *context, const std::string &label) void Sampler::setLabel(const Context *context, const std::string &label)
{ {
...@@ -39,7 +45,7 @@ const std::string &Sampler::getLabel() const ...@@ -39,7 +45,7 @@ const std::string &Sampler::getLabel() const
void Sampler::setMinFilter(const Context *context, GLenum minFilter) void Sampler::setMinFilter(const Context *context, GLenum minFilter)
{ {
mState.setMinFilter(minFilter); mState.setMinFilter(minFilter);
onStateChange(angle::SubjectMessage::DirtyBitsFlagged); signalDirtyState();
} }
GLenum Sampler::getMinFilter() const GLenum Sampler::getMinFilter() const
...@@ -50,7 +56,7 @@ GLenum Sampler::getMinFilter() const ...@@ -50,7 +56,7 @@ GLenum Sampler::getMinFilter() const
void Sampler::setMagFilter(const Context *context, GLenum magFilter) void Sampler::setMagFilter(const Context *context, GLenum magFilter)
{ {
mState.setMagFilter(magFilter); mState.setMagFilter(magFilter);
onStateChange(angle::SubjectMessage::DirtyBitsFlagged); signalDirtyState();
} }
GLenum Sampler::getMagFilter() const GLenum Sampler::getMagFilter() const
...@@ -61,7 +67,7 @@ GLenum Sampler::getMagFilter() const ...@@ -61,7 +67,7 @@ GLenum Sampler::getMagFilter() const
void Sampler::setWrapS(const Context *context, GLenum wrapS) void Sampler::setWrapS(const Context *context, GLenum wrapS)
{ {
mState.setWrapS(wrapS); mState.setWrapS(wrapS);
onStateChange(angle::SubjectMessage::DirtyBitsFlagged); signalDirtyState();
} }
GLenum Sampler::getWrapS() const GLenum Sampler::getWrapS() const
...@@ -72,7 +78,7 @@ GLenum Sampler::getWrapS() const ...@@ -72,7 +78,7 @@ GLenum Sampler::getWrapS() const
void Sampler::setWrapT(const Context *context, GLenum wrapT) void Sampler::setWrapT(const Context *context, GLenum wrapT)
{ {
mState.setWrapT(wrapT); mState.setWrapT(wrapT);
onStateChange(angle::SubjectMessage::DirtyBitsFlagged); signalDirtyState();
} }
GLenum Sampler::getWrapT() const GLenum Sampler::getWrapT() const
...@@ -83,7 +89,7 @@ GLenum Sampler::getWrapT() const ...@@ -83,7 +89,7 @@ GLenum Sampler::getWrapT() const
void Sampler::setWrapR(const Context *context, GLenum wrapR) void Sampler::setWrapR(const Context *context, GLenum wrapR)
{ {
mState.setWrapR(wrapR); mState.setWrapR(wrapR);
onStateChange(angle::SubjectMessage::DirtyBitsFlagged); signalDirtyState();
} }
GLenum Sampler::getWrapR() const GLenum Sampler::getWrapR() const
...@@ -94,7 +100,7 @@ GLenum Sampler::getWrapR() const ...@@ -94,7 +100,7 @@ GLenum Sampler::getWrapR() const
void Sampler::setMaxAnisotropy(const Context *context, float maxAnisotropy) void Sampler::setMaxAnisotropy(const Context *context, float maxAnisotropy)
{ {
mState.setMaxAnisotropy(maxAnisotropy); mState.setMaxAnisotropy(maxAnisotropy);
onStateChange(angle::SubjectMessage::DirtyBitsFlagged); signalDirtyState();
} }
float Sampler::getMaxAnisotropy() const float Sampler::getMaxAnisotropy() const
...@@ -105,7 +111,7 @@ float Sampler::getMaxAnisotropy() const ...@@ -105,7 +111,7 @@ float Sampler::getMaxAnisotropy() const
void Sampler::setMinLod(const Context *context, GLfloat minLod) void Sampler::setMinLod(const Context *context, GLfloat minLod)
{ {
mState.setMinLod(minLod); mState.setMinLod(minLod);
onStateChange(angle::SubjectMessage::DirtyBitsFlagged); signalDirtyState();
} }
GLfloat Sampler::getMinLod() const GLfloat Sampler::getMinLod() const
...@@ -116,7 +122,7 @@ GLfloat Sampler::getMinLod() const ...@@ -116,7 +122,7 @@ GLfloat Sampler::getMinLod() const
void Sampler::setMaxLod(const Context *context, GLfloat maxLod) void Sampler::setMaxLod(const Context *context, GLfloat maxLod)
{ {
mState.setMaxLod(maxLod); mState.setMaxLod(maxLod);
onStateChange(angle::SubjectMessage::DirtyBitsFlagged); signalDirtyState();
} }
GLfloat Sampler::getMaxLod() const GLfloat Sampler::getMaxLod() const
...@@ -127,7 +133,7 @@ GLfloat Sampler::getMaxLod() const ...@@ -127,7 +133,7 @@ GLfloat Sampler::getMaxLod() const
void Sampler::setCompareMode(const Context *context, GLenum compareMode) void Sampler::setCompareMode(const Context *context, GLenum compareMode)
{ {
mState.setCompareMode(compareMode); mState.setCompareMode(compareMode);
onStateChange(angle::SubjectMessage::DirtyBitsFlagged); signalDirtyState();
} }
GLenum Sampler::getCompareMode() const GLenum Sampler::getCompareMode() const
...@@ -138,7 +144,7 @@ GLenum Sampler::getCompareMode() const ...@@ -138,7 +144,7 @@ GLenum Sampler::getCompareMode() const
void Sampler::setCompareFunc(const Context *context, GLenum compareFunc) void Sampler::setCompareFunc(const Context *context, GLenum compareFunc)
{ {
mState.setCompareFunc(compareFunc); mState.setCompareFunc(compareFunc);
onStateChange(angle::SubjectMessage::DirtyBitsFlagged); signalDirtyState();
} }
GLenum Sampler::getCompareFunc() const GLenum Sampler::getCompareFunc() const
...@@ -149,7 +155,7 @@ GLenum Sampler::getCompareFunc() const ...@@ -149,7 +155,7 @@ GLenum Sampler::getCompareFunc() const
void Sampler::setSRGBDecode(const Context *context, GLenum sRGBDecode) void Sampler::setSRGBDecode(const Context *context, GLenum sRGBDecode)
{ {
mState.setSRGBDecode(sRGBDecode); mState.setSRGBDecode(sRGBDecode);
onStateChange(angle::SubjectMessage::DirtyBitsFlagged); signalDirtyState();
} }
GLenum Sampler::getSRGBDecode() const GLenum Sampler::getSRGBDecode() const
...@@ -160,7 +166,7 @@ GLenum Sampler::getSRGBDecode() const ...@@ -160,7 +166,7 @@ GLenum Sampler::getSRGBDecode() const
void Sampler::setBorderColor(const Context *context, const ColorGeneric &color) void Sampler::setBorderColor(const Context *context, const ColorGeneric &color)
{ {
mState.setBorderColor(color); mState.setBorderColor(color);
onStateChange(angle::SubjectMessage::DirtyBitsFlagged); signalDirtyState();
} }
const ColorGeneric &Sampler::getBorderColor() const const ColorGeneric &Sampler::getBorderColor() const
...@@ -175,13 +181,20 @@ const SamplerState &Sampler::getSamplerState() const ...@@ -175,13 +181,20 @@ const SamplerState &Sampler::getSamplerState() const
rx::SamplerImpl *Sampler::getImplementation() const rx::SamplerImpl *Sampler::getImplementation() const
{ {
return mImpl; return mSampler;
} }
angle::Result Sampler::syncState(const Context *context) angle::Result Sampler::syncState(const Context *context)
{ {
// TODO(jmadill): Use actual dirty bits for sampler. ASSERT(isDirty());
return mImpl->syncState(context); return mSampler->syncState(context, mDirty);
mDirty = false;
}
void Sampler::signalDirtyState()
{
mDirty = true;
onStateChange(angle::SubjectMessage::DirtyBitsFlagged);
} }
} // namespace gl } // namespace gl
...@@ -76,10 +76,13 @@ class Sampler final : public RefCountObject, public LabeledObject, public angle: ...@@ -76,10 +76,13 @@ class Sampler final : public RefCountObject, public LabeledObject, public angle:
rx::SamplerImpl *getImplementation() const; rx::SamplerImpl *getImplementation() const;
angle::Result syncState(const Context *context); angle::Result syncState(const Context *context);
bool isDirty() const { return mDirty; }
private: private:
void signalDirtyState();
SamplerState mState; SamplerState mState;
rx::SamplerImpl *mImpl; bool mDirty;
rx::SamplerImpl *mSampler;
std::string mLabel; std::string mLabel;
}; };
......
...@@ -2687,7 +2687,7 @@ angle::Result State::syncSamplers(const Context *context) ...@@ -2687,7 +2687,7 @@ angle::Result State::syncSamplers(const Context *context)
for (size_t samplerIndex : mDirtySamplers) for (size_t samplerIndex : mDirtySamplers)
{ {
BindingPointer<Sampler> &sampler = mSamplers[samplerIndex]; BindingPointer<Sampler> &sampler = mSamplers[samplerIndex];
if (sampler.get()) if (sampler.get() && sampler->isDirty())
{ {
ANGLE_TRY(sampler->syncState(context)); ANGLE_TRY(sampler->syncState(context));
} }
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "common/angleutils.h" #include "common/angleutils.h"
#include "libANGLE/Error.h" #include "libANGLE/Error.h"
#include "libANGLE/Sampler.h"
namespace gl namespace gl
{ {
...@@ -27,7 +28,11 @@ class SamplerImpl : angle::NonCopyable ...@@ -27,7 +28,11 @@ class SamplerImpl : angle::NonCopyable
SamplerImpl(const gl::SamplerState &state) : mState(state) {} SamplerImpl(const gl::SamplerState &state) : mState(state) {}
virtual ~SamplerImpl() {} virtual ~SamplerImpl() {}
virtual angle::Result syncState(const gl::Context *context) = 0; virtual void onDestroy(const gl::Context *context)
{
// Default implementation: no-op.
}
virtual angle::Result syncState(const gl::Context *context, const bool dirty) = 0;
protected: protected:
const gl::SamplerState &mState; const gl::SamplerState &mState;
......
...@@ -20,10 +20,10 @@ class SamplerD3D : public SamplerImpl ...@@ -20,10 +20,10 @@ class SamplerD3D : public SamplerImpl
SamplerD3D(const gl::SamplerState &state) : SamplerImpl(state) {} SamplerD3D(const gl::SamplerState &state) : SamplerImpl(state) {}
~SamplerD3D() override {} ~SamplerD3D() override {}
angle::Result syncState(const gl::Context *context) override; angle::Result syncState(const gl::Context *context, const bool dirtyBits) override;
}; };
inline angle::Result SamplerD3D::syncState(const gl::Context *context) inline angle::Result SamplerD3D::syncState(const gl::Context *context, const bool dirtyBits)
{ {
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -83,8 +83,12 @@ SamplerGL::~SamplerGL() ...@@ -83,8 +83,12 @@ SamplerGL::~SamplerGL()
mSamplerID = 0; mSamplerID = 0;
} }
angle::Result SamplerGL::syncState(const gl::Context *context) angle::Result SamplerGL::syncState(const gl::Context *context, const bool dirty)
{ {
if (!dirty)
{
return angle::Result::Continue;
}
// clang-format off // clang-format off
SyncSamplerStateMember(mFunctions, mSamplerID, mState, mAppliedSamplerState, GL_TEXTURE_MIN_FILTER, &gl::SamplerState::getMinFilter, &gl::SamplerState::setMinFilter); SyncSamplerStateMember(mFunctions, mSamplerID, mState, mAppliedSamplerState, GL_TEXTURE_MIN_FILTER, &gl::SamplerState::getMinFilter, &gl::SamplerState::setMinFilter);
SyncSamplerStateMember(mFunctions, mSamplerID, mState, mAppliedSamplerState, GL_TEXTURE_MAG_FILTER, &gl::SamplerState::getMagFilter, &gl::SamplerState::setMagFilter); SyncSamplerStateMember(mFunctions, mSamplerID, mState, mAppliedSamplerState, GL_TEXTURE_MAG_FILTER, &gl::SamplerState::getMagFilter, &gl::SamplerState::setMagFilter);
......
...@@ -26,7 +26,7 @@ class SamplerGL : public SamplerImpl ...@@ -26,7 +26,7 @@ class SamplerGL : public SamplerImpl
StateManagerGL *stateManager); StateManagerGL *stateManager);
~SamplerGL() override; ~SamplerGL() override;
angle::Result syncState(const gl::Context *context) override; angle::Result syncState(const gl::Context *context, const bool dirty) override;
GLuint getSamplerID() const; GLuint getSamplerID() const;
......
...@@ -18,7 +18,7 @@ SamplerNULL::SamplerNULL(const gl::SamplerState &state) : SamplerImpl(state) {} ...@@ -18,7 +18,7 @@ SamplerNULL::SamplerNULL(const gl::SamplerState &state) : SamplerImpl(state) {}
SamplerNULL::~SamplerNULL() {} SamplerNULL::~SamplerNULL() {}
angle::Result SamplerNULL::syncState(const gl::Context *context) angle::Result SamplerNULL::syncState(const gl::Context *context, const bool dirty)
{ {
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -21,7 +21,7 @@ class SamplerNULL : public SamplerImpl ...@@ -21,7 +21,7 @@ class SamplerNULL : public SamplerImpl
SamplerNULL(const gl::SamplerState &state); SamplerNULL(const gl::SamplerState &state);
~SamplerNULL() override; ~SamplerNULL() override;
angle::Result syncState(const gl::Context *context) override; angle::Result syncState(const gl::Context *context, const bool dirty) override;
}; };
} // namespace rx } // namespace rx
......
...@@ -105,6 +105,9 @@ constexpr size_t kInFlightCommandsLimit = 100u; ...@@ -105,6 +105,9 @@ constexpr size_t kInFlightCommandsLimit = 100u;
// Initially dumping the command graphs is disabled. // Initially dumping the command graphs is disabled.
constexpr bool kEnableCommandGraphDiagnostics = false; constexpr bool kEnableCommandGraphDiagnostics = false;
// Used as fallback serial for null sampler objects
constexpr Serial kZeroSerial = Serial();
void InitializeSubmitInfo(VkSubmitInfo *submitInfo, void InitializeSubmitInfo(VkSubmitInfo *submitInfo,
const vk::PrimaryCommandBuffer &commandBuffer, const vk::PrimaryCommandBuffer &commandBuffer,
const std::vector<VkSemaphore> &waitSemaphores, const std::vector<VkSemaphore> &waitSemaphores,
...@@ -293,7 +296,7 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk ...@@ -293,7 +296,7 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk
mGraphicsDirtyBits = mNewGraphicsCommandBufferDirtyBits; mGraphicsDirtyBits = mNewGraphicsCommandBufferDirtyBits;
mComputeDirtyBits = mNewComputeCommandBufferDirtyBits; mComputeDirtyBits = mNewComputeCommandBufferDirtyBits;
mActiveTextures.fill(nullptr); mActiveTextures.fill({nullptr, nullptr});
mPipelineDirtyBitsMask.set(); mPipelineDirtyBitsMask.set();
mPipelineDirtyBitsMask.reset(gl::State::DIRTY_BIT_TEXTURE_BINDINGS); mPipelineDirtyBitsMask.reset(gl::State::DIRTY_BIT_TEXTURE_BINDINGS);
...@@ -2433,6 +2436,7 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context, ...@@ -2433,6 +2436,7 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context,
for (size_t textureUnit : activeTextures) for (size_t textureUnit : activeTextures)
{ {
gl::Texture *texture = textures[textureUnit]; gl::Texture *texture = textures[textureUnit];
gl::Sampler *sampler = mState.getSampler(textureUnit);
gl::TextureType textureType = textureTypes[textureUnit]; gl::TextureType textureType = textureTypes[textureUnit];
// Null textures represent incomplete textures. // Null textures represent incomplete textures.
...@@ -2442,6 +2446,7 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context, ...@@ -2442,6 +2446,7 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context,
} }
TextureVk *textureVk = vk::GetImpl(texture); TextureVk *textureVk = vk::GetImpl(texture);
SamplerVk *samplerVk = (sampler != nullptr) ? vk::GetImpl(sampler) : nullptr;
vk::ImageHelper &image = textureVk->getImage(); vk::ImageHelper &image = textureVk->getImage();
...@@ -2473,14 +2478,18 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context, ...@@ -2473,14 +2478,18 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context,
image.addReadDependency(recorder); image.addReadDependency(recorder);
mActiveTextures[textureUnit] = textureVk; mActiveTextures[textureUnit].texture = textureVk;
mActiveTexturesDesc.update(textureUnit, textureVk->getSerial()); mActiveTextures[textureUnit].sampler = samplerVk;
// Cache serials from sampler and texture, but re-use texture if no sampler bound
ASSERT(textureVk != nullptr);
mActiveTexturesDesc.update(textureUnit, textureVk->getSerial(),
(samplerVk != nullptr) ? samplerVk->getSerial() : kZeroSerial);
} }
return angle::Result::Continue; return angle::Result::Continue;
} }
const gl::ActiveTextureArray<TextureVk *> &ContextVk::getActiveTextures() const const gl::ActiveTextureArray<vk::TextureUnit> &ContextVk::getActiveTextures() const
{ {
return mActiveTextures; return mActiveTextures;
} }
......
...@@ -223,7 +223,7 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO ...@@ -223,7 +223,7 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO
const char *file, const char *file,
const char *function, const char *function,
unsigned int line) override; unsigned int line) override;
const gl::ActiveTextureArray<TextureVk *> &getActiveTextures() const; const gl::ActiveTextureArray<vk::TextureUnit> &getActiveTextures() const;
void setIndexBufferDirty() void setIndexBufferDirty()
{ {
...@@ -557,7 +557,7 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO ...@@ -557,7 +557,7 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO
// This cache should also probably include the texture index (shader location) and array // This cache should also probably include the texture index (shader location) and array
// index (also in the shader). This info is used in the descriptor update step. // index (also in the shader). This info is used in the descriptor update step.
gl::ActiveTextureArray<TextureVk *> mActiveTextures; gl::ActiveTextureArray<vk::TextureUnit> mActiveTextures;
vk::TextureDescriptorDesc mActiveTexturesDesc; vk::TextureDescriptorDesc mActiveTexturesDesc;
// "Current Value" aka default vertex attribute state. // "Current Value" aka default vertex attribute state.
......
...@@ -1435,7 +1435,7 @@ angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk) ...@@ -1435,7 +1435,7 @@ angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk)
gl::ActiveTextureArray<VkWriteDescriptorSet> writeDescriptorInfo; gl::ActiveTextureArray<VkWriteDescriptorSet> writeDescriptorInfo;
uint32_t writeCount = 0; uint32_t writeCount = 0;
const gl::ActiveTextureArray<TextureVk *> &activeTextures = contextVk->getActiveTextures(); const gl::ActiveTextureArray<vk::TextureUnit> &activeTextures = contextVk->getActiveTextures();
bool emulateSeamfulCubeMapSampling = contextVk->emulateSeamfulCubeMapSampling(); bool emulateSeamfulCubeMapSampling = contextVk->emulateSeamfulCubeMapSampling();
...@@ -1450,13 +1450,16 @@ angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk) ...@@ -1450,13 +1450,16 @@ angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk)
++arrayElement) ++arrayElement)
{ {
GLuint textureUnit = samplerBinding.boundTextureUnits[arrayElement]; GLuint textureUnit = samplerBinding.boundTextureUnits[arrayElement];
TextureVk *textureVk = activeTextures[textureUnit]; TextureVk *textureVk = activeTextures[textureUnit].texture;
SamplerVk *samplerVk = activeTextures[textureUnit].sampler;
vk::ImageHelper &image = textureVk->getImage(); vk::ImageHelper &image = textureVk->getImage();
VkDescriptorImageInfo &imageInfo = descriptorImageInfo[writeCount]; VkDescriptorImageInfo &imageInfo = descriptorImageInfo[writeCount];
imageInfo.sampler = textureVk->getSampler().getHandle(); // Use bound sampler object if one present, otherwise use texture's sampler
imageInfo.sampler = (samplerVk != nullptr) ? samplerVk->getSampler().getHandle()
: textureVk->getSampler().getHandle();
imageInfo.imageView = textureVk->getReadImageView().getHandle(); imageInfo.imageView = textureVk->getReadImageView().getHandle();
imageInfo.imageLayout = image.getCurrentLayout(); imageInfo.imageLayout = image.getCurrentLayout();
......
...@@ -10,16 +10,68 @@ ...@@ -10,16 +10,68 @@
#include "libANGLE/renderer/vulkan/SamplerVk.h" #include "libANGLE/renderer/vulkan/SamplerVk.h"
#include "common/debug.h" #include "common/debug.h"
#include "libANGLE/Context.h"
#include "libANGLE/renderer/vulkan/vk_utils.h"
namespace rx namespace rx
{ {
SamplerVk::SamplerVk(const gl::SamplerState &state) : SamplerImpl(state) {} SamplerVk::SamplerVk(const gl::SamplerState &state) : SamplerImpl(state), mSerial{} {}
SamplerVk::~SamplerVk() {} SamplerVk::~SamplerVk() = default;
angle::Result SamplerVk::syncState(const gl::Context *context) void SamplerVk::onDestroy(const gl::Context *context)
{ {
ContextVk *contextVk = vk::GetImpl(context);
contextVk->releaseObject(contextVk->getCurrentQueueSerial(), &mSampler);
}
const vk::Sampler &SamplerVk::getSampler() const
{
ASSERT(mSampler.valid());
return mSampler;
}
angle::Result SamplerVk::syncState(const gl::Context *context, const bool dirty)
{
ContextVk *contextVk = vk::GetImpl(context);
RendererVk *renderer = contextVk->getRenderer();
if (mSampler.valid())
{
if (!dirty)
{
return angle::Result::Continue;
}
contextVk->releaseObject(contextVk->getCurrentQueueSerial(), &mSampler);
}
const gl::Extensions &extensions = renderer->getNativeExtensions();
float maxAnisotropy = mState.getMaxAnisotropy();
bool anisotropyEnable = extensions.textureFilterAnisotropic && maxAnisotropy > 1.0f;
VkSamplerCreateInfo samplerInfo = {};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.flags = 0;
samplerInfo.magFilter = gl_vk::GetFilter(mState.getMagFilter());
samplerInfo.minFilter = gl_vk::GetFilter(mState.getMinFilter());
samplerInfo.mipmapMode = gl_vk::GetSamplerMipmapMode(mState.getMinFilter());
samplerInfo.addressModeU = gl_vk::GetSamplerAddressMode(mState.getWrapS());
samplerInfo.addressModeV = gl_vk::GetSamplerAddressMode(mState.getWrapT());
samplerInfo.addressModeW = gl_vk::GetSamplerAddressMode(mState.getWrapR());
samplerInfo.mipLodBias = 0.0f;
samplerInfo.anisotropyEnable = anisotropyEnable;
samplerInfo.maxAnisotropy = maxAnisotropy;
samplerInfo.compareEnable = mState.getCompareMode() == GL_COMPARE_REF_TO_TEXTURE;
samplerInfo.compareOp = gl_vk::GetCompareOp(mState.getCompareFunc());
samplerInfo.minLod = mState.getMinLod();
samplerInfo.maxLod = mState.getMaxLod();
samplerInfo.borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
ANGLE_VK_TRY(contextVk, mSampler.init(contextVk->getDevice(), samplerInfo));
// Regenerate the serial on a sampler change.
mSerial = contextVk->generateTextureSerial();
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
#define LIBANGLE_RENDERER_VULKAN_SAMPLERVK_H_ #define LIBANGLE_RENDERER_VULKAN_SAMPLERVK_H_
#include "libANGLE/renderer/SamplerImpl.h" #include "libANGLE/renderer/SamplerImpl.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/vk_helpers.h"
namespace rx namespace rx
{ {
...@@ -21,7 +23,15 @@ class SamplerVk : public SamplerImpl ...@@ -21,7 +23,15 @@ class SamplerVk : public SamplerImpl
SamplerVk(const gl::SamplerState &state); SamplerVk(const gl::SamplerState &state);
~SamplerVk() override; ~SamplerVk() override;
angle::Result syncState(const gl::Context *context) override; void onDestroy(const gl::Context *context) override;
angle::Result syncState(const gl::Context *context, const bool dirty) override;
const vk::Sampler &getSampler() const;
Serial getSerial() const { return mSerial; }
private:
vk::Sampler mSampler;
// The serial is used for cache indexing.
Serial mSerial;
}; };
} // namespace rx } // namespace rx
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "libANGLE/renderer/TextureImpl.h" #include "libANGLE/renderer/TextureImpl.h"
#include "libANGLE/renderer/vulkan/CommandGraph.h" #include "libANGLE/renderer/vulkan/CommandGraph.h"
#include "libANGLE/renderer/vulkan/RenderTargetVk.h" #include "libANGLE/renderer/vulkan/RenderTargetVk.h"
#include "libANGLE/renderer/vulkan/SamplerVk.h"
#include "libANGLE/renderer/vulkan/vk_helpers.h" #include "libANGLE/renderer/vulkan/vk_helpers.h"
namespace rx namespace rx
......
...@@ -1516,7 +1516,7 @@ void PipelineHelper::addTransition(GraphicsPipelineTransitionBits bits, ...@@ -1516,7 +1516,7 @@ void PipelineHelper::addTransition(GraphicsPipelineTransitionBits bits,
TextureDescriptorDesc::TextureDescriptorDesc() : mMaxIndex(0) TextureDescriptorDesc::TextureDescriptorDesc() : mMaxIndex(0)
{ {
mSerials.fill(0); mSerials.fill({0, 0});
} }
TextureDescriptorDesc::~TextureDescriptorDesc() = default; TextureDescriptorDesc::~TextureDescriptorDesc() = default;
...@@ -1524,7 +1524,7 @@ TextureDescriptorDesc::TextureDescriptorDesc(const TextureDescriptorDesc &other) ...@@ -1524,7 +1524,7 @@ TextureDescriptorDesc::TextureDescriptorDesc(const TextureDescriptorDesc &other)
TextureDescriptorDesc &TextureDescriptorDesc::operator=(const TextureDescriptorDesc &other) = TextureDescriptorDesc &TextureDescriptorDesc::operator=(const TextureDescriptorDesc &other) =
default; default;
void TextureDescriptorDesc::update(size_t index, Serial serial) void TextureDescriptorDesc::update(size_t index, Serial textureSerial, Serial samplerSerial)
{ {
if (index >= mMaxIndex) if (index >= mMaxIndex)
{ {
...@@ -1533,13 +1533,15 @@ void TextureDescriptorDesc::update(size_t index, Serial serial) ...@@ -1533,13 +1533,15 @@ void TextureDescriptorDesc::update(size_t index, Serial serial)
// If the serial number overflows we should defragment and regenerate all serials. // If the serial number overflows we should defragment and regenerate all serials.
// There should never be more than UINT_MAX textures alive at a time. // There should never be more than UINT_MAX textures alive at a time.
ASSERT(serial.getValue() < std::numeric_limits<uint32_t>::max()); ASSERT(textureSerial.getValue() < std::numeric_limits<uint32_t>::max());
mSerials[index] = static_cast<uint32_t>(serial.getValue()); ASSERT(samplerSerial.getValue() < std::numeric_limits<uint32_t>::max());
mSerials[index].texture = static_cast<uint32_t>(textureSerial.getValue());
mSerials[index].sampler = static_cast<uint32_t>(samplerSerial.getValue());
} }
size_t TextureDescriptorDesc::hash() const size_t TextureDescriptorDesc::hash() const
{ {
return angle::ComputeGenericHash(&mSerials, sizeof(uint32_t) * mMaxIndex); return angle::ComputeGenericHash(&mSerials, sizeof(TexUnitSerials) * mMaxIndex);
} }
void TextureDescriptorDesc::reset() void TextureDescriptorDesc::reset()
...@@ -1556,7 +1558,7 @@ bool TextureDescriptorDesc::operator==(const TextureDescriptorDesc &other) const ...@@ -1556,7 +1558,7 @@ bool TextureDescriptorDesc::operator==(const TextureDescriptorDesc &other) const
if (mMaxIndex == 0) if (mMaxIndex == 0)
return true; return true;
return memcmp(mSerials.data(), other.mSerials.data(), sizeof(uint32_t) * mMaxIndex) == 0; return memcmp(mSerials.data(), other.mSerials.data(), sizeof(TexUnitSerials) * mMaxIndex) == 0;
} }
} // namespace vk } // namespace vk
......
...@@ -696,7 +696,7 @@ class TextureDescriptorDesc ...@@ -696,7 +696,7 @@ class TextureDescriptorDesc
TextureDescriptorDesc(const TextureDescriptorDesc &other); TextureDescriptorDesc(const TextureDescriptorDesc &other);
TextureDescriptorDesc &operator=(const TextureDescriptorDesc &other); TextureDescriptorDesc &operator=(const TextureDescriptorDesc &other);
void update(size_t index, Serial serial); void update(size_t index, Serial textureSerial, Serial samplerSerial);
size_t hash() const; size_t hash() const;
void reset(); void reset();
...@@ -707,7 +707,12 @@ class TextureDescriptorDesc ...@@ -707,7 +707,12 @@ class TextureDescriptorDesc
private: private:
uint32_t mMaxIndex; uint32_t mMaxIndex;
gl::ActiveTextureArray<uint32_t> mSerials; struct TexUnitSerials
{
uint32_t texture;
uint32_t sampler;
};
gl::ActiveTextureArray<TexUnitSerials> mSerials;
}; };
} // namespace vk } // namespace vk
} // namespace rx } // namespace rx
......
...@@ -32,6 +32,12 @@ constexpr VkBufferUsageFlags kStagingBufferFlags = ...@@ -32,6 +32,12 @@ constexpr VkBufferUsageFlags kStagingBufferFlags =
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
constexpr size_t kStagingBufferSize = 1024 * 16; constexpr size_t kStagingBufferSize = 1024 * 16;
struct TextureUnit final
{
TextureVk *texture;
SamplerVk *sampler;
};
// A dynamic buffer is conceptually an infinitely long buffer. Each time you write to the buffer, // A dynamic buffer is conceptually an infinitely long buffer. Each time you write to the buffer,
// you will always write to a previously unused portion. After a series of writes, you must flush // you will always write to a previously unused portion. After a series of writes, you must flush
// the buffer data to the device. Buffer lifetime currently assumes that each new allocation will // the buffer data to the device. Buffer lifetime currently assumes that each new allocation will
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
PROC(MemoryObject) \ PROC(MemoryObject) \
PROC(Query) \ PROC(Query) \
PROC(Program) \ PROC(Program) \
PROC(Sampler) \
PROC(Semaphore) \ PROC(Semaphore) \
PROC(Texture) \ PROC(Texture) \
PROC(TransformFeedback) \ PROC(TransformFeedback) \
......
...@@ -620,14 +620,6 @@ ...@@ -620,14 +620,6 @@
3430 VULKAN : dEQP-GLES3.functional.shaders.linkage.varying.struct.u* = FAIL 3430 VULKAN : dEQP-GLES3.functional.shaders.linkage.varying.struct.u* = FAIL
3430 VULKAN : dEQP-GLES3.functional.shaders.linkage.varying.struct.float_uvec2_vec3 = FAIL 3430 VULKAN : dEQP-GLES3.functional.shaders.linkage.varying.struct.float_uvec2_vec3 = FAIL
// Sampler objects:
3208 VULKAN : dEQP-GLES3.functional.samplers.single_tex_2d.* = FAIL
3208 VULKAN : dEQP-GLES3.functional.samplers.multi_tex_2d.* = FAIL
3208 VULKAN : dEQP-GLES3.functional.samplers.single_tex_3d.* = FAIL
3208 VULKAN : dEQP-GLES3.functional.samplers.multi_tex_3d.* = FAIL
3208 VULKAN : dEQP-GLES3.functional.samplers.single_cubemap.* = FAIL
3208 VULKAN : dEQP-GLES3.functional.samplers.multi_cubemap.* = FAIL
// Instanced rendering: // Instanced rendering:
2672 VULKAN : dEQP-GLES3.functional.draw.draw_arrays_instanced.line_loop.* = SKIP 2672 VULKAN : dEQP-GLES3.functional.draw.draw_arrays_instanced.line_loop.* = SKIP
2672 VULKAN : dEQP-GLES3.functional.draw.draw_elements_instanced.line_loop.* = SKIP 2672 VULKAN : dEQP-GLES3.functional.draw.draw_elements_instanced.line_loop.* = SKIP
......
...@@ -2871,9 +2871,6 @@ TEST_P(SimpleStateChangeTest, ReleaseShaderInUseThatReadsFromUniforms) ...@@ -2871,9 +2871,6 @@ TEST_P(SimpleStateChangeTest, ReleaseShaderInUseThatReadsFromUniforms)
// Tests that sampler sync isn't masked by program textures. // Tests that sampler sync isn't masked by program textures.
TEST_P(SimpleStateChangeTestES3, SamplerSyncNotTiedToProgram) TEST_P(SimpleStateChangeTestES3, SamplerSyncNotTiedToProgram)
{ {
// glBindSampler is available only if the GLES version is 3.0 or higher - anglebug.com/3208
ANGLE_SKIP_TEST_IF(IsVulkan());
// Create a sampler with NEAREST filtering. // Create a sampler with NEAREST filtering.
GLSampler sampler; GLSampler sampler;
glBindSampler(0, sampler); glBindSampler(0, sampler);
...@@ -2935,6 +2932,104 @@ void main() ...@@ -2935,6 +2932,104 @@ void main()
EXPECT_PIXEL_RECT_EQ(kHalfSize, kHalfSize, kHalfSize, kHalfSize, GLColor::yellow); EXPECT_PIXEL_RECT_EQ(kHalfSize, kHalfSize, kHalfSize, kHalfSize, GLColor::yellow);
} }
// Tests different samplers can be used with same texture obj on different tex units.
TEST_P(SimpleStateChangeTestES3, MultipleSamplersWithSingleTextureObject)
{
// Test overview - Create two separate sampler objects, initially with the same
// sampling args (NEAREST). Bind the same texture object to separate texture units.
// FS samples from two samplers and blends result.
// Bind separate sampler objects to the same texture units as the texture object.
// Render & verify initial results
// Next modify sampler0 to have LINEAR filtering instead of NEAREST
// Render and save results
// Now restore sampler0 to NEAREST filtering and make sampler1 LINEAR
// Render and verify results are the same as previous
// Create 2 samplers with NEAREST filtering.
constexpr GLsizei kNumSamplers = 2;
// We create/bind an extra sampler w/o bound tex object for testing purposes
GLSampler samplers[kNumSamplers + 1];
// Set samplers to initially have same state w/ NEAREST filter mode
for (uint32_t i = 0; i < kNumSamplers + 1; ++i)
{
glSamplerParameteri(samplers[i], GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glSamplerParameteri(samplers[i], GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(samplers[i], GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glSamplerParameteri(samplers[i], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glSamplerParameteri(samplers[i], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glSamplerParameterf(samplers[i], GL_TEXTURE_MAX_LOD, 1000);
glSamplerParameterf(samplers[i], GL_TEXTURE_MIN_LOD, -1000);
glBindSampler(i, samplers[i]);
ASSERT_GL_NO_ERROR();
}
// Create a simple texture with four colors
constexpr GLsizei kSize = 2;
std::array<GLColor, 4> pixels = {
{GLColor::red, GLColor::green, GLColor::blue, GLColor::yellow}};
GLTexture rgbyTex;
// Bind same texture object to tex units 0 & 1
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, rgbyTex);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, rgbyTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
pixels.data());
// Create a program that uses the texture with 2 separate samplers.
constexpr char kFS[] = R"(precision mediump float;
varying vec2 v_texCoord;
uniform sampler2D samp1;
uniform sampler2D samp2;
void main()
{
gl_FragColor = mix(texture2D(samp1, v_texCoord), texture2D(samp2, v_texCoord), 0.5);
})";
// Create program and bind samplers to tex units 0 & 1
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), kFS);
GLint s1loc = glGetUniformLocation(program, "samp1");
GLint s2loc = glGetUniformLocation(program, "samp2");
glUseProgram(program);
glUniform1i(s1loc, 0);
glUniform1i(s2loc, 1);
// Draw. This first draw is a sanitycheck and not really necessary for the test
drawQuad(program, std::string(essl1_shaders::PositionAttrib()), 0.5f);
ASSERT_GL_NO_ERROR();
constexpr int kHalfSize = kWindowSize / 2;
// When rendering w/ NEAREST, colors are all maxed out so should still be solid
EXPECT_PIXEL_RECT_EQ(0, 0, kHalfSize, kHalfSize, GLColor::red);
EXPECT_PIXEL_RECT_EQ(kHalfSize, 0, kHalfSize, kHalfSize, GLColor::green);
EXPECT_PIXEL_RECT_EQ(0, kHalfSize, kHalfSize, kHalfSize, GLColor::blue);
EXPECT_PIXEL_RECT_EQ(kHalfSize, kHalfSize, kHalfSize, kHalfSize, GLColor::yellow);
// Make first sampler use linear filtering
glSamplerParameteri(samplers[0], GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(samplers[0], GL_TEXTURE_MAG_FILTER, GL_LINEAR);
drawQuad(program, std::string(essl1_shaders::PositionAttrib()), 0.5f);
ASSERT_GL_NO_ERROR();
// Capture rendered pixel color with s0 linear
std::vector<GLColor> s0LinearColors(kWindowSize * kWindowSize);
glReadPixels(0, 0, kWindowSize, kWindowSize, GL_RGBA, GL_UNSIGNED_BYTE, s0LinearColors.data());
// Now restore first sampler & update second sampler
glSamplerParameteri(samplers[0], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glSamplerParameteri(samplers[0], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glSamplerParameteri(samplers[1], GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(samplers[1], GL_TEXTURE_MAG_FILTER, GL_LINEAR);
drawQuad(program, std::string(essl1_shaders::PositionAttrib()), 0.5f);
ASSERT_GL_NO_ERROR();
// Capture rendered pixel color w/ s1 linear
std::vector<GLColor> s1LinearColors(kWindowSize * kWindowSize);
glReadPixels(0, 0, kWindowSize, kWindowSize, GL_RGBA, GL_UNSIGNED_BYTE, s1LinearColors.data());
// Results should be the same regardless of if s0 or s1 is linear
EXPECT_EQ(s0LinearColors, s1LinearColors);
}
// Tests that deleting an in-flight image texture does not immediately delete the resource. // Tests that deleting an in-flight image texture does not immediately delete the resource.
TEST_P(SimpleStateChangeTestES31, DeleteImageTextureInUse) TEST_P(SimpleStateChangeTestES31, DeleteImageTextureInUse)
{ {
......
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