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";
// Remove the uniform declaration from the tree so it isn't parsed again.
TIntermSequence emptyReplacement;
mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(
getParentNode()->getAsBlock(), node, emptyReplacement));
} }
mInDefaultUniform = false; mInDefaultUniform = false;
// Remove the uniform declaration from the tree so it isn't parsed again.
TIntermSequence emptyReplacement;
mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(getParentNode()->getAsBlock(),
node, emptyReplacement));
} }
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
......
...@@ -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