Commit 65ec0b2e by Xinghua Cao Committed by Commit Bot

ES31: Add support for bindImageTexture on GL backend

This patch refers to https://chromium-review.googlesource.com/c/380636/ BUG=angleproject:1987 Change-Id: If621eed6ecaa7298214843a2a133801ca1487b03 Reviewed-on: https://chromium-review.googlesource.com/462088 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 7a20b973
......@@ -1066,6 +1066,18 @@ void Context::bindSampler(GLuint textureUnit, GLuint samplerHandle)
mGLState.setSamplerBinding(this, textureUnit, sampler);
}
void Context::bindImageTexture(GLuint unit,
GLuint texture,
GLint level,
GLboolean layered,
GLint layer,
GLenum access,
GLenum format)
{
Texture *tex = mState.mTextures->getTexture(texture);
mGLState.setImageUnit(this, unit, tex, level, layered, layer, access, format);
}
void Context::bindGenericUniformBuffer(GLuint bufferHandle)
{
Buffer *buffer = mState.mBuffers->checkBufferAllocation(mImplementation.get(), bufferHandle);
......
......@@ -136,6 +136,13 @@ class Context final : public ValidationContext
GLintptr offset,
GLsizei stride);
void bindSampler(GLuint textureUnit, GLuint samplerHandle);
void bindImageTexture(GLuint unit,
GLuint texture,
GLint level,
GLboolean layered,
GLint layer,
GLenum access,
GLenum format);
void bindGenericUniformBuffer(GLuint bufferHandle);
void bindIndexedUniformBuffer(GLuint bufferHandle,
GLuint index,
......
......@@ -198,10 +198,9 @@ LinkResult MemoryProgramCache::Deserialize(const Context *context,
"All bits of DrawBufferMask can be contained in an uint32_t");
state->mActiveOutputVariables = stream.readInt<uint32_t>();
unsigned int low = stream.readInt<unsigned int>();
unsigned int high = stream.readInt<unsigned int>();
state->mSamplerUniformRange = RangeUI(low, high);
unsigned int samplerRangeLow = stream.readInt<unsigned int>();
unsigned int samplerRangeHigh = stream.readInt<unsigned int>();
state->mSamplerUniformRange = RangeUI(samplerRangeLow, samplerRangeHigh);
unsigned int samplerCount = stream.readInt<unsigned int>();
for (unsigned int samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex)
{
......@@ -210,6 +209,17 @@ LinkResult MemoryProgramCache::Deserialize(const Context *context,
state->mSamplerBindings.emplace_back(SamplerBinding(textureType, bindingCount));
}
unsigned int imageRangeLow = stream.readInt<unsigned int>();
unsigned int imageRangeHigh = stream.readInt<unsigned int>();
state->mImageUniformRange = RangeUI(imageRangeLow, imageRangeHigh);
unsigned int imageCount = stream.readInt<unsigned int>();
for (unsigned int imageIndex = 0; imageIndex < imageCount; ++imageIndex)
{
GLuint boundImageUnit = stream.readInt<unsigned int>();
size_t elementCount = stream.readInt<size_t>();
state->mImageBindings.emplace_back(ImageBinding(boundImageUnit, elementCount));
}
return program->getImplementation()->load(context, infoLog, &stream);
}
......@@ -343,6 +353,16 @@ void MemoryProgramCache::Serialize(const Context *context,
stream.writeInt(samplerBinding.boundTextureUnits.size());
}
stream.writeInt(state.getImageUniformRange().low());
stream.writeInt(state.getImageUniformRange().high());
stream.writeInt(state.getImageBindings().size());
for (const auto &imageBinding : state.getImageBindings())
{
stream.writeInt(imageBinding.boundImageUnit);
stream.writeInt(imageBinding.elementCount);
}
program->getImplementation()->save(&stream);
ASSERT(binaryOut);
......
......@@ -1736,17 +1736,43 @@ bool Program::linkUniforms(const Context *context,
linker.getResults(&mState.mUniforms, &mState.mUniformLocations);
linkSamplerBindings();
linkSamplerAndImageBindings();
return true;
}
void Program::linkSamplerBindings()
void Program::linkSamplerAndImageBindings()
{
unsigned int high = static_cast<unsigned int>(mState.mUniforms.size());
unsigned int low = high;
for (auto samplerIter = mState.mUniforms.rbegin();
for (auto imageIter = mState.mUniforms.rbegin();
imageIter != mState.mUniforms.rend() && imageIter->isImage(); ++imageIter)
{
--low;
}
mState.mImageUniformRange = RangeUI(low, high);
// If uniform is a image type, insert it into the mImageBindings array.
for (unsigned int imageIndex : mState.mImageUniformRange)
{
// ES3.1 (section 7.6.1) and GLSL ES3.1 (section 4.4.5), Uniform*i{v}
// commands cannot load values into a uniform defined as an image,
// if declare without a binding qualifier, the image variable is
// initially bound to unit zero.
auto &imageUniform = mState.mUniforms[imageIndex];
if (imageUniform.binding == -1)
{
imageUniform.binding = 0;
}
mState.mImageBindings.emplace_back(
ImageBinding(imageUniform.binding, imageUniform.elementCount()));
}
high = low;
for (auto samplerIter = mState.mUniforms.rbegin() + mState.mImageUniformRange.length();
samplerIter != mState.mUniforms.rend() && samplerIter->isSampler(); ++samplerIter)
{
--low;
......
......@@ -197,6 +197,14 @@ struct TransformFeedbackVarying : public sh::Varying
GLuint arrayIndex;
};
struct ImageBinding
{
ImageBinding(GLuint imageUnit, size_t count) : boundImageUnit(imageUnit), elementCount(count) {}
GLuint boundImageUnit;
size_t elementCount;
};
class ProgramState final : angle::NonCopyable
{
public:
......@@ -234,8 +242,10 @@ class ProgramState final : angle::NonCopyable
const std::vector<VariableLocation> &getUniformLocations() const { return mUniformLocations; }
const std::vector<UniformBlock> &getUniformBlocks() const { return mUniformBlocks; }
const std::vector<SamplerBinding> &getSamplerBindings() const { return mSamplerBindings; }
const std::vector<ImageBinding> &getImageBindings() const { return mImageBindings; }
const sh::WorkGroupSize &getComputeShaderLocalSize() const { return mComputeShaderLocalSize; }
const RangeUI &getSamplerUniformRange() const { return mSamplerUniformRange; }
const RangeUI &getImageUniformRange() const { return mImageUniformRange; }
const std::vector<TransformFeedbackVarying> &getLinkedTransformFeedbackVaryings() const
{
......@@ -281,10 +291,14 @@ class ProgramState final : angle::NonCopyable
std::vector<VariableLocation> mUniformLocations;
std::vector<UniformBlock> mUniformBlocks;
RangeUI mSamplerUniformRange;
RangeUI mImageUniformRange;
// An array of the samplers that are used by the program
std::vector<gl::SamplerBinding> mSamplerBindings;
// An array of the images that are used by the program
std::vector<gl::ImageBinding> mImageBindings;
std::vector<sh::OutputVariable> mOutputVariables;
// TODO(jmadill): use unordered/hash map when available
std::map<int, VariableLocation> mOutputLocations;
......@@ -460,6 +474,9 @@ class Program final : angle::NonCopyable, public LabeledObject
{
return mState.mSamplerBindings;
}
const std::vector<ImageBinding> &getImageBindings() const { return mState.mImageBindings; }
const ProgramState &getState() const { return mState; }
static bool linkValidateVariablesBase(InfoLog &infoLog,
......@@ -519,7 +536,7 @@ class Program final : angle::NonCopyable, public LabeledObject
bool linkUniforms(const Context *context,
InfoLog &infoLog,
const Bindings &uniformLocationBindings);
void linkSamplerBindings();
void linkSamplerAndImageBindings();
bool areMatchingInterfaceBlocks(InfoLog &infoLog,
const sh::InterfaceBlock &vertexInterfaceBlock,
......
......@@ -146,6 +146,7 @@ void State::initialize(const Context *context,
mAtomicCounterBuffers.resize(caps.maxAtomicCounterBufferBindings);
mShaderStorageBuffers.resize(caps.maxShaderStorageBufferBindings);
mImageUnits.resize(caps.maxImageUnits);
}
if (extensions.eglImageExternal || extensions.eglStreamConsumerExternal)
{
......@@ -202,6 +203,16 @@ void State::reset(const Context *context)
mSamplers[samplerIdx].set(context, nullptr);
}
for (auto &imageUnit : mImageUnits)
{
imageUnit.texture.set(context, nullptr);
imageUnit.level = 0;
imageUnit.layered = false;
imageUnit.layer = 0;
imageUnit.access = GL_READ_ONLY;
imageUnit.format = GL_R32UI;
}
mArrayBuffer.set(context, nullptr);
mDrawIndirectBuffer.set(context, nullptr);
mRenderbuffer.set(context, nullptr);
......@@ -794,6 +805,20 @@ void State::detachTexture(const Context *context, const TextureMap &zeroTextures
}
}
for (auto &bindingImageUnit : mImageUnits)
{
if (bindingImageUnit.texture.id() == texture)
{
bindingImageUnit.texture.set(context, nullptr);
bindingImageUnit.level = 0;
bindingImageUnit.layered = false;
bindingImageUnit.layer = 0;
bindingImageUnit.access = GL_READ_ONLY;
bindingImageUnit.format = GL_R32UI;
break;
}
}
// [OpenGL ES 2.0.24] section 4.4 page 112:
// If a texture object is deleted while its image is attached to the currently bound framebuffer, then it is
// as if Texture2DAttachment had been called, with a texture of 0, for each attachment point to which this
......@@ -2174,4 +2199,26 @@ void State::setObjectDirty(GLenum target)
}
}
void State::setImageUnit(const Context *context,
GLuint unit,
Texture *texture,
GLint level,
GLboolean layered,
GLint layer,
GLenum access,
GLenum format)
{
mImageUnits[unit].texture.set(context, texture);
mImageUnits[unit].level = level;
mImageUnits[unit].layered = layered;
mImageUnits[unit].layer = layer;
mImageUnits[unit].access = access;
mImageUnits[unit].format = format;
}
const ImageUnit &State::getImageUnit(GLuint unit) const
{
return mImageUnits[unit];
}
} // namespace gl
......@@ -456,6 +456,17 @@ class State : angle::NonCopyable
void syncDirtyObject(const Context *context, GLenum target);
void setObjectDirty(GLenum target);
void setImageUnit(const Context *context,
GLuint unit,
Texture *texture,
GLint level,
GLboolean layered,
GLint layer,
GLenum access,
GLenum format);
const ImageUnit &getImageUnit(GLuint unit) const;
private:
// Cached values from Context's caps
GLuint mMaxDrawBuffers;
......@@ -512,6 +523,9 @@ class State : angle::NonCopyable
typedef std::vector<BindingPointer<Sampler>> SamplerBindingVector;
SamplerBindingVector mSamplers;
typedef std::vector<ImageUnit> ImageUnitVector;
ImageUnitVector mImageUnits;
typedef std::map<GLenum, BindingPointer<Query>> ActiveQueryMap;
ActiveQueryMap mActiveQueries;
......
......@@ -325,35 +325,45 @@ bool UniformLinker::flattenUniformsAndCheckCapsForShader(
Shader *shader,
GLuint maxUniformComponents,
GLuint maxTextureImageUnits,
GLuint maxImageUnits,
const std::string &componentsErrorMessage,
const std::string &samplerErrorMessage,
const std::string &imageErrorMessage,
std::vector<LinkedUniform> &samplerUniforms,
std::vector<LinkedUniform> &imageUniforms,
InfoLog &infoLog)
{
VectorAndSamplerCount vasCount;
ShaderUniformCount shaderUniformCount;
for (const sh::Uniform &uniform : shader->getUniforms(context))
{
vasCount += flattenUniform(uniform, &samplerUniforms);
shaderUniformCount += flattenUniform(uniform, &samplerUniforms, &imageUniforms);
}
if (vasCount.vectorCount > maxUniformComponents)
if (shaderUniformCount.vectorCount > maxUniformComponents)
{
infoLog << componentsErrorMessage << maxUniformComponents << ").";
return false;
}
if (vasCount.samplerCount > maxTextureImageUnits)
if (shaderUniformCount.samplerCount > maxTextureImageUnits)
{
infoLog << samplerErrorMessage << maxTextureImageUnits << ").";
return false;
}
if (shaderUniformCount.imageCount > maxImageUnits)
{
infoLog << imageErrorMessage << maxImageUnits << ").";
return false;
}
return true;
}
bool UniformLinker::flattenUniformsAndCheckCaps(const Context *context, InfoLog &infoLog)
{
std::vector<LinkedUniform> samplerUniforms;
std::vector<LinkedUniform> imageUniforms;
const Caps &caps = context->getCaps();
......@@ -364,10 +374,11 @@ bool UniformLinker::flattenUniformsAndCheckCaps(const Context *context, InfoLog
// TODO (mradev): check whether we need finer-grained component counting
if (!flattenUniformsAndCheckCapsForShader(
context, computeShader, caps.maxComputeUniformComponents / 4,
caps.maxComputeTextureImageUnits,
caps.maxComputeTextureImageUnits, caps.maxComputeImageUniforms,
"Compute shader active uniforms exceed MAX_COMPUTE_UNIFORM_COMPONENTS (",
"Compute shader sampler count exceeds MAX_COMPUTE_TEXTURE_IMAGE_UNITS (",
samplerUniforms, infoLog))
"Compute shader image count exceeds MAX_COMPUTE_IMAGE_UNIFORMS (", samplerUniforms,
imageUniforms, infoLog))
{
return false;
}
......@@ -378,10 +389,11 @@ bool UniformLinker::flattenUniformsAndCheckCaps(const Context *context, InfoLog
if (!flattenUniformsAndCheckCapsForShader(
context, vertexShader, caps.maxVertexUniformVectors,
caps.maxVertexTextureImageUnits,
caps.maxVertexTextureImageUnits, caps.maxVertexImageUniforms,
"Vertex shader active uniforms exceed MAX_VERTEX_UNIFORM_VECTORS (",
"Vertex shader sampler count exceeds MAX_VERTEX_TEXTURE_IMAGE_UNITS (",
samplerUniforms, infoLog))
"Vertex shader image count exceeds MAX_VERTEX_IMAGE_UNIFORMS (", samplerUniforms,
imageUniforms, infoLog))
{
return false;
}
......@@ -390,42 +402,48 @@ bool UniformLinker::flattenUniformsAndCheckCaps(const Context *context, InfoLog
if (!flattenUniformsAndCheckCapsForShader(
context, fragmentShader, caps.maxFragmentUniformVectors, caps.maxTextureImageUnits,
caps.maxFragmentImageUniforms,
"Fragment shader active uniforms exceed MAX_FRAGMENT_UNIFORM_VECTORS (",
"Fragment shader sampler count exceeds MAX_TEXTURE_IMAGE_UNITS (", samplerUniforms,
infoLog))
"Fragment shader sampler count exceeds MAX_TEXTURE_IMAGE_UNITS (",
"Fragment shader image count exceeds MAX_FRAGMENT_IMAGE_UNIFORMS (",
samplerUniforms, imageUniforms, infoLog))
{
return false;
}
}
mUniforms.insert(mUniforms.end(), samplerUniforms.begin(), samplerUniforms.end());
mUniforms.insert(mUniforms.end(), imageUniforms.begin(), imageUniforms.end());
return true;
}
UniformLinker::VectorAndSamplerCount UniformLinker::flattenUniform(
UniformLinker::ShaderUniformCount UniformLinker::flattenUniform(
const sh::Uniform &uniform,
std::vector<LinkedUniform> *samplerUniforms)
std::vector<LinkedUniform> *samplerUniforms,
std::vector<LinkedUniform> *imageUniforms)
{
int location = uniform.location;
VectorAndSamplerCount uniformVasCount = flattenUniformImpl(
uniform, uniform.name, samplerUniforms, uniform.staticUse, uniform.binding, &location);
ShaderUniformCount shaderUniformCount =
flattenUniformImpl(uniform, uniform.name, samplerUniforms, imageUniforms, uniform.staticUse,
uniform.binding, &location);
if (uniform.staticUse)
{
return uniformVasCount;
return shaderUniformCount;
}
return VectorAndSamplerCount();
return ShaderUniformCount();
}
UniformLinker::VectorAndSamplerCount UniformLinker::flattenUniformImpl(
UniformLinker::ShaderUniformCount UniformLinker::flattenUniformImpl(
const sh::ShaderVariable &uniform,
const std::string &fullName,
std::vector<LinkedUniform> *samplerUniforms,
std::vector<LinkedUniform> *imageUniforms,
bool markStaticUse,
int binding,
int *location)
{
ASSERT(location);
VectorAndSamplerCount vectorAndSamplerCount;
ShaderUniformCount shaderUniformCount;
if (uniform.isStruct())
{
......@@ -438,22 +456,28 @@ UniformLinker::VectorAndSamplerCount UniformLinker::flattenUniformImpl(
const sh::ShaderVariable &field = uniform.fields[fieldIndex];
const std::string &fieldFullName = (fullName + elementString + "." + field.name);
vectorAndSamplerCount += flattenUniformImpl(field, fieldFullName, samplerUniforms,
markStaticUse, -1, location);
shaderUniformCount +=
flattenUniformImpl(field, fieldFullName, samplerUniforms, imageUniforms,
markStaticUse, -1, location);
}
}
return vectorAndSamplerCount;
return shaderUniformCount;
}
// Not a struct
bool isSampler = IsSamplerType(uniform.type);
bool isImage = IsImageType(uniform.type);
std::vector<gl::LinkedUniform> *uniformList = &mUniforms;
if (isSampler)
{
// Store sampler uniforms separately, so we'll append them to the end of the list.
uniformList = samplerUniforms;
}
else if (isImage)
{
uniformList = imageUniforms;
}
LinkedUniform *existingUniform = FindUniform(*uniformList, fullName);
if (existingUniform)
{
......@@ -481,18 +505,19 @@ UniformLinker::VectorAndSamplerCount UniformLinker::flattenUniformImpl(
unsigned int elementCount = uniform.elementCount();
// Samplers aren't "real" uniforms, so they don't count towards register usage.
// Likewise, don't count "real" uniforms towards sampler count.
vectorAndSamplerCount.vectorCount =
(isSampler ? 0 : (VariableRegisterCount(uniform.type) * elementCount));
vectorAndSamplerCount.samplerCount = (isSampler ? elementCount : 0);
// Samplers and images aren't "real" uniforms, so they don't count towards register usage.
// Likewise, don't count "real" uniforms towards sampler and image count.
shaderUniformCount.vectorCount =
((isSampler || isImage) ? 0 : (VariableRegisterCount(uniform.type) * elementCount));
shaderUniformCount.samplerCount = (isSampler ? elementCount : 0);
shaderUniformCount.imageCount = (isImage ? elementCount : 0);
if (*location != -1)
{
*location += elementCount;
}
return vectorAndSamplerCount;
return shaderUniformCount;
}
} // namespace gl
......@@ -30,21 +30,23 @@ class UniformLinker
std::vector<VariableLocation> *uniformLocations);
private:
struct VectorAndSamplerCount
struct ShaderUniformCount
{
VectorAndSamplerCount() : vectorCount(0), samplerCount(0) {}
VectorAndSamplerCount(const VectorAndSamplerCount &other) = default;
VectorAndSamplerCount &operator=(const VectorAndSamplerCount &other) = default;
ShaderUniformCount() : vectorCount(0), samplerCount(0), imageCount(0) {}
ShaderUniformCount(const ShaderUniformCount &other) = default;
ShaderUniformCount &operator=(const ShaderUniformCount &other) = default;
VectorAndSamplerCount &operator+=(const VectorAndSamplerCount &other)
ShaderUniformCount &operator+=(const ShaderUniformCount &other)
{
vectorCount += other.vectorCount;
samplerCount += other.samplerCount;
imageCount += other.imageCount;
return *this;
}
unsigned int vectorCount;
unsigned int samplerCount;
unsigned int imageCount;
};
bool validateVertexAndFragmentUniforms(const Context *context, InfoLog &infoLog) const;
......@@ -58,23 +60,29 @@ class UniformLinker
Shader *shader,
GLuint maxUniformComponents,
GLuint maxTextureImageUnits,
GLuint maxImageUnits,
const std::string &componentsErrorMessage,
const std::string &samplerErrorMessage,
const std::string &imageErrorMessage,
std::vector<LinkedUniform> &samplerUniforms,
std::vector<LinkedUniform> &imageUniforms,
InfoLog &infoLog);
bool flattenUniformsAndCheckCaps(const Context *context, InfoLog &infoLog);
VectorAndSamplerCount flattenUniform(const sh::Uniform &uniform,
std::vector<LinkedUniform> *samplerUniforms);
ShaderUniformCount flattenUniform(const sh::Uniform &uniform,
std::vector<LinkedUniform> *samplerUniforms,
std::vector<LinkedUniform> *imageUniforms);
// markStaticUse is given as a separate parameter because it is tracked here at struct
// granularity.
VectorAndSamplerCount flattenUniformImpl(const sh::ShaderVariable &uniform,
const std::string &fullName,
std::vector<LinkedUniform> *samplerUniforms,
bool markStaticUse,
int binding,
int *location);
ShaderUniformCount flattenUniformImpl(const sh::ShaderVariable &uniform,
const std::string &fullName,
std::vector<LinkedUniform> *samplerUniforms,
std::vector<LinkedUniform> *imageUniforms,
bool markStaticUse,
int binding,
int *location);
bool indexUniforms(InfoLog &infoLog, const Program::Bindings &uniformLocationBindings);
bool gatherUniformLocationsAndCheckConflicts(InfoLog &infoLog,
......
......@@ -21,6 +21,7 @@
namespace gl
{
class Buffer;
class Texture;
enum PrimitiveType
{
......@@ -237,6 +238,21 @@ struct DrawElementsIndirectCommand
static_assert(sizeof(DrawElementsIndirectCommand) == 20,
"Unexpected size of DrawElementsIndirectCommand");
struct ImageUnit
{
ImageUnit()
: texture(), level(0), layered(false), layer(0), access(GL_READ_ONLY), format(GL_R32UI)
{
}
BindingPointer<Texture> texture;
GLint level;
GLboolean layered;
GLint layer;
GLenum access;
GLenum format;
};
struct PixelStoreStateBase : private angle::NonCopyable
{
BindingPointer<Buffer> pixelBuffer;
......
......@@ -50,6 +50,7 @@ StateManagerGL::StateManagerGL(const FunctionsGL *functions, const gl::Caps &ren
mTextureUnitIndex(0),
mTextures(),
mSamplers(rendererCaps.maxCombinedTextureImageUnits, 0),
mImages(rendererCaps.maxImageUnits, ImageUnitBinding()),
mTransformFeedback(0),
mQueries(),
mPrevDrawTransformFeedback(nullptr),
......@@ -206,6 +207,15 @@ void StateManagerGL::deleteTexture(GLuint texture)
}
}
for (size_t imageUnitIndex = 0; imageUnitIndex < mImages.size(); imageUnitIndex++)
{
if (mImages[imageUnitIndex].texture == texture)
{
bindImageTexture(static_cast<GLuint>(imageUnitIndex), 0, 0, false, 0, GL_READ_ONLY,
GL_R32UI);
}
}
mFunctions->deleteTextures(1, &texture);
}
}
......@@ -407,6 +417,28 @@ void StateManagerGL::bindSampler(size_t unit, GLuint sampler)
}
}
void StateManagerGL::bindImageTexture(GLuint unit,
GLuint texture,
GLint level,
GLboolean layered,
GLint layer,
GLenum access,
GLenum format)
{
auto &binding = mImages[unit];
if (binding.texture != texture || binding.level != level || binding.layered != layered ||
binding.layer != layer || binding.access != access || binding.format != format)
{
binding.texture = texture;
binding.level = level;
binding.layered = layered;
binding.layer = layer;
binding.access = access;
binding.format = format;
mFunctions->bindImageTexture(unit, texture, level, layered, layer, access, format);
}
}
void StateManagerGL::setPixelUnpackState(const gl::PixelUnpackState &unpack)
{
GLuint unpackBufferID = 0;
......@@ -845,6 +877,32 @@ void StateManagerGL::setGenericShaderState(const gl::Context *context)
}
}
}
// TODO(xinghua.cao@intel.com): Track image units state with dirty bits to
// avoid update every draw call.
ASSERT(context->getClientVersion() >= gl::ES_3_1 || program->getImageBindings().size() == 0);
for (const gl::ImageBinding &imageUniform : program->getImageBindings())
{
for (size_t imageUnitIndex = 0; imageUnitIndex < imageUniform.elementCount;
imageUnitIndex++)
{
const gl::ImageUnit &imageUnit = glState.getImageUnit(
imageUniform.boundImageUnit + static_cast<GLuint>(imageUnitIndex));
const TextureGL *textureGL = SafeGetImplAs<TextureGL>(imageUnit.texture.get());
if (textureGL)
{
bindImageTexture(imageUniform.boundImageUnit + static_cast<GLuint>(imageUnitIndex),
textureGL->getTextureID(), imageUnit.level, imageUnit.layered,
imageUnit.layer, imageUnit.access, imageUnit.format);
}
else
{
bindImageTexture(imageUniform.boundImageUnit + static_cast<GLuint>(imageUnitIndex),
0, imageUnit.level, imageUnit.layered, imageUnit.layer,
imageUnit.access, imageUnit.format);
}
}
}
}
gl::Error StateManagerGL::setGenericDrawState(const gl::Context *context)
......
......@@ -56,6 +56,13 @@ class StateManagerGL final : angle::NonCopyable
void activeTexture(size_t unit);
void bindTexture(GLenum type, GLuint texture);
void bindSampler(size_t unit, GLuint sampler);
void bindImageTexture(GLuint unit,
GLuint texture,
GLint level,
GLboolean layered,
GLint layer,
GLenum access,
GLenum format);
void bindFramebuffer(GLenum type, GLuint framebuffer);
void bindRenderbuffer(GLenum type, GLuint renderbuffer);
void bindTransformFeedback(GLenum type, GLuint transformFeedback);
......@@ -196,6 +203,22 @@ class StateManagerGL final : angle::NonCopyable
std::map<GLenum, std::vector<GLuint>> mTextures;
std::vector<GLuint> mSamplers;
struct ImageUnitBinding
{
ImageUnitBinding()
: texture(0), level(0), layered(false), layer(0), access(GL_READ_ONLY), format(GL_R32UI)
{
}
GLuint texture;
GLint level;
GLboolean layered;
GLint layer;
GLenum access;
GLenum format;
};
std::vector<ImageUnitBinding> mImages;
GLuint mTransformFeedback;
std::map<GLenum, GLuint> mQueries;
......
......@@ -816,4 +816,83 @@ bool ValidateDispatchCompute(Context *context,
return true;
}
bool ValidateBindImageTexture(Context *context,
GLuint unit,
GLuint texture,
GLint level,
GLboolean layered,
GLint layer,
GLenum access,
GLenum format)
{
GLuint maxImageUnits = context->getCaps().maxImageUnits;
if (unit >= maxImageUnits)
{
context->handleError(InvalidValue()
<< "unit cannot be greater than or equal than MAX_IMAGE_UNITS = "
<< maxImageUnits);
return false;
}
if (level < 0)
{
context->handleError(InvalidValue() << "level is negative.");
return false;
}
if (layer < 0)
{
context->handleError(InvalidValue() << "layer is negative.");
return false;
}
if (access != GL_READ_ONLY && access != GL_WRITE_ONLY && access != GL_READ_WRITE)
{
context->handleError(InvalidEnum() << "access is not one of the supported tokens.");
return false;
}
switch (format)
{
case GL_RGBA32F:
case GL_RGBA16F:
case GL_R32F:
case GL_RGBA32UI:
case GL_RGBA16UI:
case GL_RGBA8UI:
case GL_R32UI:
case GL_RGBA32I:
case GL_RGBA16I:
case GL_RGBA8I:
case GL_R32I:
case GL_RGBA8:
case GL_RGBA8_SNORM:
break;
default:
context->handleError(InvalidValue()
<< "format is not one of supported image unit formats.");
return false;
}
if (texture != 0)
{
Texture *tex = context->getTexture(texture);
if (tex == nullptr)
{
context->handleError(InvalidValue()
<< "texture is not the name of an existing texture object.");
return false;
}
if (!tex->getImmutableFormat())
{
context->handleError(InvalidOperation()
<< "texture is not the name of an immutable texture object.");
return false;
}
}
return true;
}
} // namespace gl
......@@ -87,6 +87,15 @@ bool ValidateDispatchCompute(Context *context,
GLuint numGroupsY,
GLuint numGroupsZ);
bool ValidateBindImageTexture(Context *context,
GLuint unit,
GLuint texture,
GLint level,
GLboolean layered,
GLint layer,
GLenum access,
GLenum format);
} // namespace gl
#endif // LIBANGLE_VALIDATION_ES31_H_
......@@ -964,11 +964,13 @@ void GL_APIENTRY BindImageTexture(GLuint unit,
Context *context = GetValidGlobalContext();
if (context)
{
if (texture != 0)
if (!context->skipValidation() && !ValidateBindImageTexture(context, unit, texture, level,
layered, layer, access, format))
{
// Binding non-zero image textures is not implemented yet.
UNIMPLEMENTED();
return;
}
context->bindImageTexture(unit, texture, level, layered, layer, access, format);
}
}
......
......@@ -243,6 +243,79 @@ TEST_P(ComputeShaderTest, DispatchCompute)
EXPECT_GL_NO_ERROR();
}
// Use image uniform to write texture in compute shader, and verify the content is expected.
TEST_P(ComputeShaderTest, BindImageTexture)
{
if (IsD3D11())
{
std::cout << "Test skipped on D3D11 because it is not implemented yet." << std::endl;
return;
}
GLTexture mTexture[2];
GLFramebuffer mFramebuffer;
const std::string csSource =
"#version 310 es\n"
"layout(local_size_x=2, local_size_y=2, local_size_z=1) in;\n"
"layout(r32ui, binding = 0) writeonly uniform highp uimage2D uImage[2];"
"void main()\n"
"{\n"
" imageStore(uImage[0], ivec2(gl_LocalInvocationIndex, gl_WorkGroupID.x), uvec4(100, 0, "
"0, 0));"
" imageStore(uImage[1], ivec2(gl_LocalInvocationIndex, gl_WorkGroupID.x), uvec4(100, 0, "
"0, 0));"
"}\n";
ANGLE_GL_COMPUTE_PROGRAM(program, csSource);
glUseProgram(program.get());
int width = 4, height = 2;
GLuint inputValues[] = {200, 200, 200, 200, 200, 200, 200, 200};
glBindTexture(GL_TEXTURE_2D, mTexture[0]);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, width, height);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED_INTEGER, GL_UNSIGNED_INT,
inputValues);
EXPECT_GL_NO_ERROR();
glBindImageTexture(0, mTexture[0], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32UI);
EXPECT_GL_NO_ERROR();
glBindTexture(GL_TEXTURE_2D, mTexture[1]);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, width, height);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED_INTEGER, GL_UNSIGNED_INT,
inputValues);
EXPECT_GL_NO_ERROR();
glBindImageTexture(1, mTexture[1], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32UI);
EXPECT_GL_NO_ERROR();
glDispatchCompute(2, 1, 1);
EXPECT_GL_NO_ERROR();
glUseProgram(0);
GLuint outputValues[2][8];
GLuint expectedValue = 100;
glBindFramebuffer(GL_READ_FRAMEBUFFER, mFramebuffer);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture[0],
0);
EXPECT_GL_NO_ERROR();
glReadPixels(0, 0, width, height, GL_RED_INTEGER, GL_UNSIGNED_INT, outputValues[0]);
EXPECT_GL_NO_ERROR();
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture[1],
0);
EXPECT_GL_NO_ERROR();
glReadPixels(0, 0, width, height, GL_RED_INTEGER, GL_UNSIGNED_INT, outputValues[1]);
EXPECT_GL_NO_ERROR();
for (int i = 0; i < width * height; i++)
{
EXPECT_EQ(expectedValue, outputValues[0][i]);
EXPECT_EQ(expectedValue, outputValues[1][i]);
}
}
// Check that it is not possible to create a compute shader when the context does not support ES
// 3.10
TEST_P(ComputeShaderTestES3, NotSupported)
......
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