Commit 76e471e9 by Jamie Madill Committed by Commit Bot

Vulkan: Implement basic uniforms.

This implementation adds one descriptor set with two bindings: one for default vertex uniforms and the other for fragment. It adds two corresponding uniform buffers, and the logic for updating the descriptor sets bound to Vulkan. It doesn't handle much in the way of synchronization and dependency management, or uniform update. If there are only vertex or fragment uniforms the empty uniform buffer is omitted from the descriptor set. If both are missing, there is no descriptor set bound. Note that as our implementation progresses we might not be able to initialize our descriptor sets at link time, due to streaming in uniform data. BUG=angleproject:2167 Change-Id: I4ce4c3879ab454114df43bfac8d87ddf817fc045 Reviewed-on: https://chromium-review.googlesource.com/706340 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarYuly Novikov <ynovikov@chromium.org>
parent fb9051a5
...@@ -60,14 +60,14 @@ class DeclareDefaultUniformsTraverser : public TIntermTraverser ...@@ -60,14 +60,14 @@ class DeclareDefaultUniformsTraverser : public TIntermTraverser
if (isUniform) if (isUniform)
{ {
(*mSink) << ";\n"; (*mSink) << ";\n";
}
mInDefaultUniform = false;
// Remove the uniform declaration from the tree so it isn't parsed again. // Remove the uniform declaration from the tree so it isn't parsed again.
TIntermSequence emptyReplacement; TIntermSequence emptyReplacement;
mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(getParentNode()->getAsBlock(), mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(
node, emptyReplacement)); getParentNode()->getAsBlock(), node, emptyReplacement));
}
mInDefaultUniform = false;
} }
return true; return true;
} }
......
...@@ -487,6 +487,8 @@ egl::Error Context::onDestroy(const egl::Display *display) ...@@ -487,6 +487,8 @@ egl::Error Context::onDestroy(const egl::Display *display)
mState.mFramebuffers->release(this); mState.mFramebuffers->release(this);
mState.mPipelines->release(this); mState.mPipelines->release(this);
mImplementation->onDestroy(this);
return egl::NoError(); return egl::NoError();
} }
......
...@@ -31,6 +31,8 @@ class ContextImpl : public GLImplFactory ...@@ -31,6 +31,8 @@ class ContextImpl : public GLImplFactory
ContextImpl(const gl::ContextState &state); ContextImpl(const gl::ContextState &state);
virtual ~ContextImpl(); virtual ~ContextImpl();
virtual void onDestroy(const gl::Context *context) {}
virtual gl::Error initialize() = 0; virtual gl::Error initialize() = 0;
// Flush and finish. // Flush and finish.
......
...@@ -53,6 +53,12 @@ VkIndexType GetVkIndexType(GLenum glIndexType) ...@@ -53,6 +53,12 @@ VkIndexType GetVkIndexType(GLenum glIndexType)
} }
} }
enum DescriptorPoolIndex : uint8_t
{
UniformBufferPool = 0,
TexturePool = 1,
};
} // anonymous namespace } // anonymous namespace
ContextVk::ContextVk(const gl::ContextState &state, RendererVk *renderer) ContextVk::ContextVk(const gl::ContextState &state, RendererVk *renderer)
...@@ -196,8 +202,37 @@ ContextVk::~ContextVk() ...@@ -196,8 +202,37 @@ ContextVk::~ContextVk()
invalidateCurrentPipeline(); invalidateCurrentPipeline();
} }
void ContextVk::onDestroy(const gl::Context *context)
{
VkDevice device = mRenderer->getDevice();
mDescriptorPool.destroy(device);
}
gl::Error ContextVk::initialize() gl::Error ContextVk::initialize()
{ {
VkDevice device = mRenderer->getDevice();
VkDescriptorPoolSize poolSizes[2];
poolSizes[UniformBufferPool].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
poolSizes[UniformBufferPool].descriptorCount = 1024;
poolSizes[TexturePool].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
poolSizes[TexturePool].descriptorCount = 1024;
VkDescriptorPoolCreateInfo descriptorPoolInfo;
descriptorPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
descriptorPoolInfo.pNext = nullptr;
descriptorPoolInfo.flags = 0;
// TODO(jmadill): Pick non-arbitrary max.
descriptorPoolInfo.maxSets = 2048;
// Reserve pools for uniform blocks and textures.
descriptorPoolInfo.poolSizeCount = 2;
descriptorPoolInfo.pPoolSizes = poolSizes;
ANGLE_TRY(mDescriptorPool.init(device, descriptorPoolInfo));
return gl::NoError(); return gl::NoError();
} }
...@@ -279,6 +314,7 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, GLenum mode) ...@@ -279,6 +314,7 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, GLenum mode)
VkDevice device = mRenderer->getDevice(); VkDevice device = mRenderer->getDevice();
const auto &state = mState.getState(); const auto &state = mState.getState();
const auto &programGL = state.getProgram(); const auto &programGL = state.getProgram();
ProgramVk *programVk = GetImplAs<ProgramVk>(programGL);
const auto &vao = state.getVertexArray(); const auto &vao = state.getVertexArray();
VertexArrayVk *vkVAO = GetImplAs<VertexArrayVk>(vao); VertexArrayVk *vkVAO = GetImplAs<VertexArrayVk>(vao);
const auto *drawFBO = state.getDrawFramebuffer(); const auto *drawFBO = state.getDrawFramebuffer();
...@@ -304,6 +340,20 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, GLenum mode) ...@@ -304,6 +340,20 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, GLenum mode)
setQueueSerial(queueSerial); setQueueSerial(queueSerial);
vkVAO->updateCurrentBufferSerials(programGL->getActiveAttribLocationsMask(), queueSerial); vkVAO->updateCurrentBufferSerials(programGL->getActiveAttribLocationsMask(), queueSerial);
// TODO(jmadill): Can probably use more dirty bits here.
ContextVk *contextVk = GetImplAs<ContextVk>(context);
ANGLE_TRY(programVk->updateUniforms(contextVk));
// Bind the graphics descriptor sets.
// TODO(jmadill): Handle multiple command buffers.
VkDescriptorSet uniformDescriptorSet = programVk->getDescriptorSet();
if (uniformDescriptorSet != VK_NULL_HANDLE)
{
const vk::PipelineLayout &pipelineLayout = programVk->getPipelineLayout();
commandBuffer->bindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1,
&uniformDescriptorSet, 0, nullptr);
}
return gl::NoError(); return gl::NoError();
} }
...@@ -834,4 +884,9 @@ gl::Error ContextVk::dispatchCompute(const gl::Context *context, ...@@ -834,4 +884,9 @@ gl::Error ContextVk::dispatchCompute(const gl::Context *context,
return gl::InternalError(); return gl::InternalError();
} }
vk::DescriptorPool *ContextVk::getDescriptorPool()
{
return &mDescriptorPool;
}
} // namespace rx } // namespace rx
...@@ -27,6 +27,8 @@ class ContextVk : public ContextImpl, public ResourceVk ...@@ -27,6 +27,8 @@ class ContextVk : public ContextImpl, public ResourceVk
gl::Error initialize() override; gl::Error initialize() override;
void onDestroy(const gl::Context *context) override;
// Flush and finish. // Flush and finish.
gl::Error flush() override; gl::Error flush() override;
gl::Error finish() override; gl::Error finish() override;
...@@ -148,6 +150,8 @@ class ContextVk : public ContextImpl, public ResourceVk ...@@ -148,6 +150,8 @@ class ContextVk : public ContextImpl, public ResourceVk
GLuint numGroupsY, GLuint numGroupsY,
GLuint numGroupsZ) override; GLuint numGroupsZ) override;
vk::DescriptorPool *getDescriptorPool();
private: private:
gl::Error initPipeline(const gl::Context *context); gl::Error initPipeline(const gl::Context *context);
gl::Error setupDraw(const gl::Context *context, GLenum mode); gl::Error setupDraw(const gl::Context *context, GLenum mode);
...@@ -171,6 +175,10 @@ class ContextVk : public ContextImpl, public ResourceVk ...@@ -171,6 +175,10 @@ class ContextVk : public ContextImpl, public ResourceVk
VkPipelineColorBlendAttachmentState mCurrentBlendAttachmentState; VkPipelineColorBlendAttachmentState mCurrentBlendAttachmentState;
VkPipelineColorBlendStateCreateInfo mCurrentBlendState; VkPipelineColorBlendStateCreateInfo mCurrentBlendState;
VkGraphicsPipelineCreateInfo mCurrentPipelineInfo; VkGraphicsPipelineCreateInfo mCurrentPipelineInfo;
// The descriptor pool is externally sychronized, so cannot be accessed from different threads
// simulataneously. Hence, we keep it in the ContextVk instead of the RendererVk.
vk::DescriptorPool mDescriptorPool;
}; };
} // namespace rx } // namespace rx
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "libANGLE/renderer/vulkan/ProgramVk.h" #include "libANGLE/renderer/vulkan/ProgramVk.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/utilities.h"
#include "libANGLE/Context.h" #include "libANGLE/Context.h"
#include "libANGLE/renderer/vulkan/ContextVk.h" #include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/GlslangWrapper.h" #include "libANGLE/renderer/vulkan/GlslangWrapper.h"
...@@ -18,7 +19,122 @@ ...@@ -18,7 +19,122 @@
namespace rx namespace rx
{ {
ProgramVk::ProgramVk(const gl::ProgramState &state) : ProgramImpl(state) namespace
{
gl::Error InitDefaultUniformBlock(const gl::Context *context,
VkDevice device,
gl::Shader *shader,
vk::BufferAndMemory *storageOut,
sh::BlockLayoutMap *blockLayoutMapOut,
size_t *requiredSizeOut)
{
const auto &uniforms = shader->getUniforms(context);
if (uniforms.empty())
{
*requiredSizeOut = 0;
return gl::NoError();
}
sh::Std140BlockEncoder blockEncoder;
sh::GetUniformBlockInfo(uniforms, "", &blockEncoder, false, blockLayoutMapOut);
size_t blockSize = blockEncoder.getBlockSize();
// TODO(jmadill): I think we still need a valid block for the pipeline even if zero sized.
if (blockSize == 0)
{
*requiredSizeOut = 0;
return gl::NoError();
}
VkBufferCreateInfo uniformBufferInfo;
uniformBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
uniformBufferInfo.pNext = nullptr;
uniformBufferInfo.flags = 0;
uniformBufferInfo.size = blockSize;
uniformBufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
uniformBufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
uniformBufferInfo.queueFamilyIndexCount = 0;
uniformBufferInfo.pQueueFamilyIndices = nullptr;
ANGLE_TRY(storageOut->buffer.init(device, uniformBufferInfo));
ANGLE_TRY(AllocateBufferMemory(GetImplAs<ContextVk>(context), blockSize, &storageOut->buffer,
&storageOut->memory, requiredSizeOut));
return gl::NoError();
}
template <typename T>
void UpdateDefaultUniformBlock(GLsizei count,
int componentCount,
const T *v,
const sh::BlockMemberInfo &layoutInfo,
angle::MemoryBuffer *uniformData)
{
// Assume an offset of -1 means the block is unused.
if (layoutInfo.offset == -1)
{
return;
}
int elementSize = sizeof(T) * componentCount;
if (layoutInfo.arrayStride == 0 || layoutInfo.arrayStride == elementSize)
{
uint8_t *writePtr = uniformData->data() + layoutInfo.offset;
memcpy(writePtr, v, elementSize * count);
}
else
{
UNIMPLEMENTED();
}
}
vk::Error SyncDefaultUniformBlock(VkDevice device,
vk::DeviceMemory *bufferMemory,
const angle::MemoryBuffer &bufferData)
{
ASSERT(bufferMemory->valid() && !bufferData.empty());
uint8_t *mapPointer = nullptr;
ANGLE_TRY(bufferMemory->map(device, 0, bufferData.size(), 0, &mapPointer));
memcpy(mapPointer, bufferData.data(), bufferData.size());
bufferMemory->unmap(device);
return vk::NoError();
}
enum ShaderIndex : uint32_t
{
MinShaderIndex = 0,
VertexShader = MinShaderIndex,
FragmentShader = 1,
MaxShaderIndex = 2,
};
gl::Shader *GetShader(const gl::ProgramState &programState, uint32_t shaderIndex)
{
switch (shaderIndex)
{
case VertexShader:
return programState.getAttachedVertexShader();
case FragmentShader:
return programState.getAttachedFragmentShader();
default:
UNREACHABLE();
return nullptr;
}
}
} // anonymous namespace
ProgramVk::DefaultUniformBlock::DefaultUniformBlock()
: storage(), uniformData(), uniformsDirty(false), uniformLayout()
{
}
ProgramVk::ProgramVk(const gl::ProgramState &state)
: ProgramImpl(state), mDefaultUniformBlocks(), mDescriptorSet(VK_NULL_HANDLE)
{ {
} }
...@@ -34,9 +150,26 @@ void ProgramVk::destroy(const gl::Context *contextImpl) ...@@ -34,9 +150,26 @@ void ProgramVk::destroy(const gl::Context *contextImpl)
void ProgramVk::reset(VkDevice device) void ProgramVk::reset(VkDevice device)
{ {
for (auto &uniformBlock : mDefaultUniformBlocks)
{
uniformBlock.storage.memory.destroy(device);
uniformBlock.storage.buffer.destroy(device);
}
mEmptyUniformBlockStorage.memory.destroy(device);
mEmptyUniformBlockStorage.buffer.destroy(device);
for (auto &descriptorSetLayout : mDescriptorSetLayouts)
{
descriptorSetLayout.destroy(device);
}
mLinkedFragmentModule.destroy(device); mLinkedFragmentModule.destroy(device);
mLinkedVertexModule.destroy(device); mLinkedVertexModule.destroy(device);
mPipelineLayout.destroy(device); mPipelineLayout.destroy(device);
// Descriptor Sets are pool allocated, so do not need to be explicitly freed.
mDescriptorSet = VK_NULL_HANDLE;
} }
gl::LinkResult ProgramVk::load(const gl::Context *contextImpl, gl::LinkResult ProgramVk::load(const gl::Context *contextImpl,
...@@ -106,34 +239,164 @@ gl::LinkResult ProgramVk::link(const gl::Context *glContext, ...@@ -106,34 +239,164 @@ gl::LinkResult ProgramVk::link(const gl::Context *glContext,
} }
ANGLE_TRY(initPipelineLayout(contextVk)); ANGLE_TRY(initPipelineLayout(contextVk));
ANGLE_TRY(initDescriptorSets(contextVk));
ANGLE_TRY(initDefaultUniformBlocks(glContext));
return true; return true;
} }
gl::Error ProgramVk::initDefaultUniformBlocks(const gl::Context *glContext)
{
ContextVk *contextVk = GetImplAs<ContextVk>(glContext);
VkDevice device = contextVk->getDevice();
// Process vertex and fragment uniforms into std140 packing.
std::array<sh::BlockLayoutMap, 2> layoutMap;
std::array<size_t, 2> requiredBufferSize = {{0, 0}};
for (uint32_t shaderIndex = MinShaderIndex; shaderIndex < MaxShaderIndex; ++shaderIndex)
{
ANGLE_TRY(InitDefaultUniformBlock(glContext, device, GetShader(mState, shaderIndex),
&mDefaultUniformBlocks[shaderIndex].storage,
&layoutMap[shaderIndex],
&requiredBufferSize[shaderIndex]));
}
// Init the default block layout info.
const auto &locations = mState.getUniformLocations();
const auto &uniforms = mState.getUniforms();
for (size_t locationIndex = 0; locationIndex < locations.size(); ++locationIndex)
{
std::array<sh::BlockMemberInfo, 2> layoutInfo;
const auto &location = locations[locationIndex];
if (location.used() && !location.ignored)
{
const auto &uniform = uniforms[location.index];
std::string uniformName = uniform.name;
if (uniform.isArray())
{
uniformName += ArrayIndexString(location.arrayIndices);
}
bool found = false;
for (uint32_t shaderIndex = MinShaderIndex; shaderIndex < MaxShaderIndex; ++shaderIndex)
{
auto it = layoutMap[shaderIndex].find(uniformName);
if (it != layoutMap[shaderIndex].end())
{
found = true;
layoutInfo[shaderIndex] = it->second;
}
}
ASSERT(found);
}
for (uint32_t shaderIndex = MinShaderIndex; shaderIndex < MaxShaderIndex; ++shaderIndex)
{
mDefaultUniformBlocks[shaderIndex].uniformLayout.push_back(layoutInfo[shaderIndex]);
}
}
bool anyDirty = false;
bool allDirty = true;
for (uint32_t shaderIndex = MinShaderIndex; shaderIndex < MaxShaderIndex; ++shaderIndex)
{
if (requiredBufferSize[shaderIndex] > 0)
{
if (!mDefaultUniformBlocks[shaderIndex].uniformData.resize(
requiredBufferSize[shaderIndex]))
{
return gl::OutOfMemory() << "Memory allocation failure.";
}
mDefaultUniformBlocks[shaderIndex].uniformData.fill(0);
mDefaultUniformBlocks[shaderIndex].uniformsDirty = true;
anyDirty = true;
}
else
{
allDirty = false;
}
}
if (anyDirty)
{
// Initialize the "empty" uniform block if necessary.
if (!allDirty)
{
VkBufferCreateInfo uniformBufferInfo;
uniformBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
uniformBufferInfo.pNext = nullptr;
uniformBufferInfo.flags = 0;
uniformBufferInfo.size = 1;
uniformBufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
uniformBufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
uniformBufferInfo.queueFamilyIndexCount = 0;
uniformBufferInfo.pQueueFamilyIndices = nullptr;
ANGLE_TRY(mEmptyUniformBlockStorage.buffer.init(device, uniformBufferInfo));
size_t requiredSize = 0;
ANGLE_TRY(AllocateBufferMemory(contextVk, 1, &mEmptyUniformBlockStorage.buffer,
&mEmptyUniformBlockStorage.memory, &requiredSize));
}
ANGLE_TRY(updateDefaultUniformsDescriptorSet(contextVk));
}
return gl::NoError();
}
GLboolean ProgramVk::validate(const gl::Caps &caps, gl::InfoLog *infoLog) GLboolean ProgramVk::validate(const gl::Caps &caps, gl::InfoLog *infoLog)
{ {
UNIMPLEMENTED(); UNIMPLEMENTED();
return GLboolean(); return GLboolean();
} }
void ProgramVk::setUniform1fv(GLint location, GLsizei count, const GLfloat *v) template <typename T>
void ProgramVk::setUniformImpl(GLint location, GLsizei count, const T *v, GLenum entryPointType)
{ {
const gl::VariableLocation &locationInfo = mState.getUniformLocations()[location];
const gl::LinkedUniform &linkedUniform = mState.getUniforms()[locationInfo.index];
if (linkedUniform.type == entryPointType)
{
for (auto &uniformBlock : mDefaultUniformBlocks)
{
const sh::BlockMemberInfo &layoutInfo = uniformBlock.uniformLayout[location];
UpdateDefaultUniformBlock(count, linkedUniform.typeInfo->componentCount, v, layoutInfo,
&uniformBlock.uniformData);
}
}
else
{
ASSERT(linkedUniform.type == gl::VariableBoolVectorType(entryPointType));
UNIMPLEMENTED(); UNIMPLEMENTED();
}
}
void ProgramVk::setUniform1fv(GLint location, GLsizei count, const GLfloat *v)
{
setUniformImpl(location, count, v, GL_FLOAT);
} }
void ProgramVk::setUniform2fv(GLint location, GLsizei count, const GLfloat *v) void ProgramVk::setUniform2fv(GLint location, GLsizei count, const GLfloat *v)
{ {
UNIMPLEMENTED(); setUniformImpl(location, count, v, GL_FLOAT_VEC2);
} }
void ProgramVk::setUniform3fv(GLint location, GLsizei count, const GLfloat *v) void ProgramVk::setUniform3fv(GLint location, GLsizei count, const GLfloat *v)
{ {
UNIMPLEMENTED(); setUniformImpl(location, count, v, GL_FLOAT_VEC3);
} }
void ProgramVk::setUniform4fv(GLint location, GLsizei count, const GLfloat *v) void ProgramVk::setUniform4fv(GLint location, GLsizei count, const GLfloat *v)
{ {
UNIMPLEMENTED(); setUniformImpl(location, count, v, GL_FLOAT_VEC4);
} }
void ProgramVk::setUniform1iv(GLint location, GLsizei count, const GLint *v) void ProgramVk::setUniform1iv(GLint location, GLsizei count, const GLint *v)
...@@ -300,13 +563,54 @@ vk::Error ProgramVk::initPipelineLayout(ContextVk *context) ...@@ -300,13 +563,54 @@ vk::Error ProgramVk::initPipelineLayout(ContextVk *context)
VkDevice device = context->getDevice(); VkDevice device = context->getDevice();
// TODO(jmadill): Descriptor sets. // Create two descriptor set layouts: one for default uniform info, and one for textures.
// Skip one or both if there are no uniforms.
VkDescriptorSetLayoutBinding uniformBindings[2];
uint32_t blockCount = 0;
{
auto &layoutBinding = uniformBindings[blockCount];
layoutBinding.binding = blockCount;
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
layoutBinding.descriptorCount = 1;
layoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
layoutBinding.pImmutableSamplers = nullptr;
blockCount++;
}
{
auto &layoutBinding = uniformBindings[blockCount];
layoutBinding.binding = blockCount;
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
layoutBinding.descriptorCount = 1;
layoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
layoutBinding.pImmutableSamplers = nullptr;
blockCount++;
}
{
VkDescriptorSetLayoutCreateInfo uniformInfo;
uniformInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
uniformInfo.pNext = nullptr;
uniformInfo.flags = 0;
uniformInfo.bindingCount = blockCount;
uniformInfo.pBindings = uniformBindings;
vk::DescriptorSetLayout uniformLayout;
ANGLE_TRY(uniformLayout.init(device, uniformInfo));
mDescriptorSetLayouts.push_back(std::move(uniformLayout));
}
VkPipelineLayoutCreateInfo createInfo; VkPipelineLayoutCreateInfo createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
createInfo.pNext = nullptr; createInfo.pNext = nullptr;
createInfo.flags = 0; createInfo.flags = 0;
createInfo.setLayoutCount = 0; createInfo.setLayoutCount = static_cast<uint32_t>(mDescriptorSetLayouts.size());
createInfo.pSetLayouts = nullptr; createInfo.pSetLayouts = mDescriptorSetLayouts[0].ptr();
createInfo.pushConstantRangeCount = 0; createInfo.pushConstantRangeCount = 0;
createInfo.pPushConstantRanges = nullptr; createInfo.pPushConstantRanges = nullptr;
...@@ -315,6 +619,29 @@ vk::Error ProgramVk::initPipelineLayout(ContextVk *context) ...@@ -315,6 +619,29 @@ vk::Error ProgramVk::initPipelineLayout(ContextVk *context)
return vk::NoError(); return vk::NoError();
} }
vk::Error ProgramVk::initDescriptorSets(ContextVk *contextVk)
{
ASSERT(mDescriptorSet == VK_NULL_HANDLE);
VkDevice device = contextVk->getDevice();
// Write out to a new a descriptor set.
// TODO(jmadill): Handle descriptor set lifetime.
vk::DescriptorPool *descriptorPool = contextVk->getDescriptorPool();
VkDescriptorSetAllocateInfo allocInfo;
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.pNext = nullptr;
allocInfo.descriptorPool = descriptorPool->getHandle();
// TODO(jmadill): Handle descriptor set layouts for textures.
allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = mDescriptorSetLayouts[0].ptr();
ANGLE_TRY(descriptorPool->allocateDescriptorSets(device, allocInfo, &mDescriptorSet));
return vk::NoError();
}
void ProgramVk::getUniformfv(const gl::Context *context, GLint location, GLfloat *params) const void ProgramVk::getUniformfv(const gl::Context *context, GLint location, GLfloat *params) const
{ {
UNIMPLEMENTED(); UNIMPLEMENTED();
...@@ -330,4 +657,79 @@ void ProgramVk::getUniformuiv(const gl::Context *context, GLint location, GLuint ...@@ -330,4 +657,79 @@ void ProgramVk::getUniformuiv(const gl::Context *context, GLint location, GLuint
UNIMPLEMENTED(); UNIMPLEMENTED();
} }
vk::Error ProgramVk::updateUniforms(ContextVk *contextVk)
{
if (!mDefaultUniformBlocks[VertexShader].uniformsDirty &&
!mDefaultUniformBlocks[FragmentShader].uniformsDirty)
{
return vk::NoError();
}
VkDevice device = contextVk->getDevice();
// Update buffer memory by immediate mapping. This immediate update only works once.
// TODO(jmadill): Handle inserting updates into the command stream, or use dynamic buffers.
for (auto &uniformBlock : mDefaultUniformBlocks)
{
if (uniformBlock.uniformsDirty)
{
ANGLE_TRY(SyncDefaultUniformBlock(device, &uniformBlock.storage.memory,
uniformBlock.uniformData));
uniformBlock.uniformsDirty = false;
}
}
return vk::NoError();
}
vk::Error ProgramVk::updateDefaultUniformsDescriptorSet(ContextVk *contextVk)
{
std::array<VkDescriptorBufferInfo, 2> descriptorBufferInfo;
std::array<VkWriteDescriptorSet, 2> writeDescriptorInfo;
uint32_t bufferCount = 0;
for (auto &uniformBlock : mDefaultUniformBlocks)
{
auto &bufferInfo = descriptorBufferInfo[bufferCount];
if (!uniformBlock.uniformData.empty())
{
bufferInfo.buffer = uniformBlock.storage.buffer.getHandle();
}
else
{
bufferInfo.buffer = mEmptyUniformBlockStorage.buffer.getHandle();
}
bufferInfo.offset = 0;
bufferInfo.range = VK_WHOLE_SIZE;
auto &writeInfo = writeDescriptorInfo[bufferCount];
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.pNext = nullptr;
writeInfo.dstSet = mDescriptorSet;
writeInfo.dstBinding = bufferCount;
writeInfo.dstArrayElement = 0;
writeInfo.descriptorCount = 1;
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
writeInfo.pImageInfo = nullptr;
writeInfo.pBufferInfo = &bufferInfo;
writeInfo.pTexelBufferView = nullptr;
bufferCount++;
}
VkDevice device = contextVk->getDevice();
vkUpdateDescriptorSets(device, bufferCount, writeDescriptorInfo.data(), 0, nullptr);
return vk::NoError();
}
VkDescriptorSet ProgramVk::getDescriptorSet() const
{
return mDescriptorSet;
}
} // namespace rx } // namespace rx
...@@ -112,13 +112,50 @@ class ProgramVk : public ProgramImpl ...@@ -112,13 +112,50 @@ class ProgramVk : public ProgramImpl
const vk::ShaderModule &getLinkedFragmentModule() const; const vk::ShaderModule &getLinkedFragmentModule() const;
const vk::PipelineLayout &getPipelineLayout() const; const vk::PipelineLayout &getPipelineLayout() const;
vk::Error updateUniforms(ContextVk *contextVk);
VkDescriptorSet getDescriptorSet() const;
private: private:
void reset(VkDevice device); void reset(VkDevice device);
vk::Error initPipelineLayout(ContextVk *context); vk::Error initPipelineLayout(ContextVk *context);
vk::Error initDescriptorSets(ContextVk *contextVk);
gl::Error initDefaultUniformBlocks(const gl::Context *glContext);
vk::Error updateDefaultUniformsDescriptorSet(ContextVk *contextVk);
template <typename T>
void setUniformImpl(GLint location, GLsizei count, const T *v, GLenum entryPointType);
vk::ShaderModule mLinkedVertexModule; vk::ShaderModule mLinkedVertexModule;
vk::ShaderModule mLinkedFragmentModule; vk::ShaderModule mLinkedFragmentModule;
vk::PipelineLayout mPipelineLayout; vk::PipelineLayout mPipelineLayout;
std::vector<vk::DescriptorSetLayout> mDescriptorSetLayouts;
// State for the default uniform blocks.
struct DefaultUniformBlock final : private angle::NonCopyable
{
DefaultUniformBlock();
vk::BufferAndMemory storage;
// Shadow copies of the shader uniform data.
angle::MemoryBuffer uniformData;
bool uniformsDirty;
// Since the default blocks are laid out in std140, this tells us where to write on a call
// to a setUniform method. They are arranged in uniform location order.
std::vector<sh::BlockMemberInfo> uniformLayout;
};
std::array<DefaultUniformBlock, 2> mDefaultUniformBlocks;
// This is a special "empty" placeholder buffer for when a shader has no uniforms.
// It is necessary because we want to keep a compatible pipeline layout in all cases,
// and Vulkan does not tolerate having null handles in a descriptor set.
vk::BufferAndMemory mEmptyUniformBlockStorage;
// Descriptor set for the uniform blocks for this program.
VkDescriptorSet mDescriptorSet;
}; };
} // namespace rx } // namespace rx
......
...@@ -463,6 +463,19 @@ void CommandBuffer::bindIndexBuffer(const vk::Buffer &buffer, ...@@ -463,6 +463,19 @@ void CommandBuffer::bindIndexBuffer(const vk::Buffer &buffer,
vkCmdBindIndexBuffer(mHandle, buffer.getHandle(), offset, indexType); vkCmdBindIndexBuffer(mHandle, buffer.getHandle(), offset, indexType);
} }
void CommandBuffer::bindDescriptorSets(VkPipelineBindPoint bindPoint,
const vk::PipelineLayout &layout,
uint32_t firstSet,
uint32_t descriptorSetCount,
const VkDescriptorSet *descriptorSets,
uint32_t dynamicOffsetCount,
const uint32_t *dynamicOffsets)
{
ASSERT(valid());
vkCmdBindDescriptorSets(mHandle, bindPoint, layout.getHandle(), firstSet, descriptorSetCount,
descriptorSets, dynamicOffsetCount, dynamicOffsets);
}
// Image implementation. // Image implementation.
Image::Image() : mCurrentLayout(VK_IMAGE_LAYOUT_UNDEFINED) Image::Image() : mCurrentLayout(VK_IMAGE_LAYOUT_UNDEFINED)
{ {
...@@ -880,6 +893,57 @@ Error PipelineLayout::init(VkDevice device, const VkPipelineLayoutCreateInfo &cr ...@@ -880,6 +893,57 @@ Error PipelineLayout::init(VkDevice device, const VkPipelineLayoutCreateInfo &cr
return NoError(); return NoError();
} }
// DescriptorSetLayout implementation.
DescriptorSetLayout::DescriptorSetLayout()
{
}
void DescriptorSetLayout::destroy(VkDevice device)
{
if (valid())
{
vkDestroyDescriptorSetLayout(device, mHandle, nullptr);
mHandle = VK_NULL_HANDLE;
}
}
Error DescriptorSetLayout::init(VkDevice device, const VkDescriptorSetLayoutCreateInfo &createInfo)
{
ASSERT(!valid());
ANGLE_VK_TRY(vkCreateDescriptorSetLayout(device, &createInfo, nullptr, &mHandle));
return NoError();
}
// DescriptorPool implementation.
DescriptorPool::DescriptorPool()
{
}
void DescriptorPool::destroy(VkDevice device)
{
if (valid())
{
vkDestroyDescriptorPool(device, mHandle, nullptr);
mHandle = VK_NULL_HANDLE;
}
}
Error DescriptorPool::init(VkDevice device, const VkDescriptorPoolCreateInfo &createInfo)
{
ASSERT(!valid());
ANGLE_VK_TRY(vkCreateDescriptorPool(device, &createInfo, nullptr, &mHandle));
return NoError();
}
Error DescriptorPool::allocateDescriptorSets(VkDevice device,
const VkDescriptorSetAllocateInfo &allocInfo,
VkDescriptorSet *descriptorSetsOut)
{
ASSERT(valid());
ANGLE_VK_TRY(vkAllocateDescriptorSets(device, &allocInfo, descriptorSetsOut));
return NoError();
}
// Fence implementation. // Fence implementation.
Fence::Fence() Fence::Fence()
{ {
......
...@@ -90,6 +90,7 @@ class DeviceMemory; ...@@ -90,6 +90,7 @@ class DeviceMemory;
class Framebuffer; class Framebuffer;
class Image; class Image;
class Pipeline; class Pipeline;
class PipelineLayout;
class RenderPass; class RenderPass;
class MemoryProperties final : angle::NonCopyable class MemoryProperties final : angle::NonCopyable
...@@ -245,6 +246,13 @@ class CommandBuffer final : public WrappedObject<CommandBuffer, VkCommandBuffer> ...@@ -245,6 +246,13 @@ class CommandBuffer final : public WrappedObject<CommandBuffer, VkCommandBuffer>
const VkBuffer *buffers, const VkBuffer *buffers,
const VkDeviceSize *offsets); const VkDeviceSize *offsets);
void bindIndexBuffer(const vk::Buffer &buffer, VkDeviceSize offset, VkIndexType indexType); void bindIndexBuffer(const vk::Buffer &buffer, VkDeviceSize offset, VkIndexType indexType);
void bindDescriptorSets(VkPipelineBindPoint bindPoint,
const vk::PipelineLayout &layout,
uint32_t firstSet,
uint32_t descriptorSetCount,
const VkDescriptorSet *descriptorSets,
uint32_t dynamicOffsetCount,
const uint32_t *dynamicOffsets);
private: private:
bool mStarted; bool mStarted;
...@@ -420,6 +428,30 @@ class PipelineLayout final : public WrappedObject<PipelineLayout, VkPipelineLayo ...@@ -420,6 +428,30 @@ class PipelineLayout final : public WrappedObject<PipelineLayout, VkPipelineLayo
Error init(VkDevice device, const VkPipelineLayoutCreateInfo &createInfo); Error init(VkDevice device, const VkPipelineLayoutCreateInfo &createInfo);
}; };
class DescriptorSetLayout final : public WrappedObject<DescriptorSetLayout, VkDescriptorSetLayout>
{
public:
DescriptorSetLayout();
void destroy(VkDevice device);
using WrappedObject::retain;
Error init(VkDevice device, const VkDescriptorSetLayoutCreateInfo &createInfo);
};
class DescriptorPool final : public WrappedObject<DescriptorPool, VkDescriptorPool>
{
public:
DescriptorPool();
void destroy(VkDevice device);
using WrappedObject::retain;
Error init(VkDevice device, const VkDescriptorPoolCreateInfo &createInfo);
Error allocateDescriptorSets(VkDevice device,
const VkDescriptorSetAllocateInfo &allocInfo,
VkDescriptorSet *descriptorSetsOut);
};
class Fence final : public WrappedObject<Fence, VkFence> class Fence final : public WrappedObject<Fence, VkFence>
{ {
public: public:
...@@ -508,6 +540,12 @@ gl::Error AllocateBufferMemory(ContextVk *contextVk, ...@@ -508,6 +540,12 @@ gl::Error AllocateBufferMemory(ContextVk *contextVk,
vk::DeviceMemory *deviceMemoryOut, vk::DeviceMemory *deviceMemoryOut,
size_t *requiredSizeOut); size_t *requiredSizeOut);
struct BufferAndMemory final : private angle::NonCopyable
{
vk::Buffer buffer;
vk::DeviceMemory memory;
};
} // namespace vk } // namespace vk
namespace gl_vk namespace gl_vk
......
...@@ -277,6 +277,104 @@ TEST_P(SimpleOperationTest, DrawIndexedQuad) ...@@ -277,6 +277,104 @@ TEST_P(SimpleOperationTest, DrawIndexedQuad)
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
} }
// Draw with a fragment uniform.
TEST_P(SimpleOperationTest, DrawQuadWithFragmentUniform)
{
const std::string &vertexShader =
"attribute vec3 position;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(position, 1);\n"
"}";
const std::string &fragmentShader =
"uniform mediump vec4 color;\n"
"void main()\n"
"{\n"
" gl_FragColor = color;\n"
"}";
ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
GLint location = glGetUniformLocation(program, "color");
ASSERT_NE(-1, location);
glUseProgram(program);
glUniform4f(location, 0.0f, 1.0f, 0.0f, 1.0f);
drawQuad(program.get(), "position", 0.5f, 1.0f, true);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Draw with a vertex uniform.
TEST_P(SimpleOperationTest, DrawQuadWithVertexUniform)
{
const std::string &vertexShader =
"attribute vec3 position;\n"
"uniform vec4 color;\n"
"varying vec4 vcolor;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(position, 1);\n"
" vcolor = color;\n"
"}";
const std::string &fragmentShader =
"varying mediump vec4 vcolor;\n"
"void main()\n"
"{\n"
" gl_FragColor = vcolor;\n"
"}";
ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
GLint location = glGetUniformLocation(program, "color");
ASSERT_NE(-1, location);
glUseProgram(program);
glUniform4f(location, 0.0f, 1.0f, 0.0f, 1.0f);
drawQuad(program.get(), "position", 0.5f, 1.0f, true);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Draw with two uniforms.
TEST_P(SimpleOperationTest, DrawQuadWithTwoUniforms)
{
const std::string &vertexShader =
"attribute vec3 position;\n"
"uniform vec4 color1;\n"
"varying vec4 vcolor1;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(position, 1);\n"
" vcolor1 = color1;\n"
"}";
const std::string &fragmentShader =
"uniform mediump vec4 color2;\n"
"varying mediump vec4 vcolor1;\n"
"void main()\n"
"{\n"
" gl_FragColor = vcolor1 + color2;\n"
"}";
ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
GLint location1 = glGetUniformLocation(program, "color1");
ASSERT_NE(-1, location1);
GLint location2 = glGetUniformLocation(program, "color2");
ASSERT_NE(-1, location2);
glUseProgram(program);
glUniform4f(location1, 0.0f, 1.0f, 0.0f, 1.0f);
glUniform4f(location2, 1.0f, 0.0f, 0.0f, 1.0f);
drawQuad(program.get(), "position", 0.5f, 1.0f, true);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow);
}
// Tests a shader program with more than one vertex attribute, with vertex buffers. // Tests a shader program with more than one vertex attribute, with vertex buffers.
TEST_P(SimpleOperationTest, ThreeVertexAttributes) TEST_P(SimpleOperationTest, ThreeVertexAttributes)
{ {
......
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