Commit 5547b384 by Jamie Madill

Vulkan: Implement simple static textures.

After this change, the SimpleTexture2D sample mostly runs. BUG=angleproject:2167 Change-Id: Ie6d56f890b1aede329e11d1e987d0f8c17a2d0b4 Reviewed-on: https://chromium-review.googlesource.com/720072Reviewed-by: 's avatarFrank Henigman <fjhenigman@chromium.org>
parent cc4ce4a2
......@@ -54,7 +54,8 @@ enum
IMPLEMENTATION_MAX_TEXTURE_LEVELS = 15,
// Limit active textures so we can use fast bitsets.
IMPLEMENTATION_MAX_ACTIVE_TEXTURES = 64,
IMPLEMENTATION_MAX_SHADER_TEXTURES = 32,
IMPLEMENTATION_MAX_ACTIVE_TEXTURES = IMPLEMENTATION_MAX_SHADER_TEXTURES * 2,
};
}
......
......@@ -343,15 +343,19 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, GLenum mode)
// TODO(jmadill): Can probably use more dirty bits here.
ContextVk *contextVk = GetImplAs<ContextVk>(context);
ANGLE_TRY(programVk->updateUniforms(contextVk));
programVk->updateTexturesDescriptorSet(contextVk);
// Bind the graphics descriptor sets.
// TODO(jmadill): Handle multiple command buffers.
VkDescriptorSet uniformDescriptorSet = programVk->getDescriptorSet();
if (uniformDescriptorSet != VK_NULL_HANDLE)
const auto &descriptorSets = programVk->getDescriptorSets();
uint32_t firstSet = programVk->getDescriptorSetOffset();
uint32_t setCount = static_cast<uint32_t>(descriptorSets.size());
if (!descriptorSets.empty() && ((setCount - firstSet) > 0))
{
const vk::PipelineLayout &pipelineLayout = programVk->getPipelineLayout();
commandBuffer->bindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1,
&uniformDescriptorSet, 0, nullptr);
commandBuffer->bindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, firstSet,
setCount - firstSet, &descriptorSets[firstSet], 0,
nullptr);
}
return gl::NoError();
......@@ -514,6 +518,7 @@ void ContextVk::syncState(const gl::Context *context, const gl::State::DirtyBits
const auto &glState = context->getGLState();
// TODO(jmadill): Full dirty bits implementation.
bool dirtyTextures = false;
for (auto dirtyBit : dirtyBits)
{
......@@ -714,13 +719,15 @@ void ContextVk::syncState(const gl::Context *context, const gl::State::DirtyBits
// Also invalidate the vertex descriptions cache in the Vertex Array.
VertexArrayVk *vaoVk = GetImplAs<VertexArrayVk>(glState.getVertexArray());
vaoVk->invalidateVertexDescriptions();
dirtyTextures = true;
break;
}
case gl::State::DIRTY_BIT_TEXTURE_BINDINGS:
WARN() << "DIRTY_BIT_TEXTURE_BINDINGS unimplemented";
dirtyTextures = true;
break;
case gl::State::DIRTY_BIT_SAMPLER_BINDINGS:
WARN() << "DIRTY_BIT_SAMPLER_BINDINGS unimplemented";
dirtyTextures = true;
break;
case gl::State::DIRTY_BIT_MULTISAMPLING:
WARN() << "DIRTY_BIT_MULTISAMPLING unimplemented";
......@@ -756,6 +763,12 @@ void ContextVk::syncState(const gl::Context *context, const gl::State::DirtyBits
break;
}
}
if (dirtyTextures)
{
ProgramVk *programVk = GetImplAs<ProgramVk>(glState.getProgram());
programVk->invalidateTextures();
}
}
GLint ContextVk::getGPUDisjoint()
......
......@@ -15,6 +15,7 @@
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/GlslangWrapper.h"
#include "libANGLE/renderer/vulkan/RendererVk.h"
#include "libANGLE/renderer/vulkan/TextureVk.h"
namespace rx
{
......@@ -134,7 +135,7 @@ ProgramVk::DefaultUniformBlock::DefaultUniformBlock()
}
ProgramVk::ProgramVk(const gl::ProgramState &state)
: ProgramImpl(state), mDefaultUniformBlocks(), mDescriptorSet(VK_NULL_HANDLE)
: ProgramImpl(state), mDefaultUniformBlocks(), mDescriptorSetOffset(0), mDirtyTextures(true)
{
}
......@@ -169,7 +170,9 @@ void ProgramVk::reset(VkDevice device)
mPipelineLayout.destroy(device);
// Descriptor Sets are pool allocated, so do not need to be explicitly freed.
mDescriptorSet = VK_NULL_HANDLE;
mDescriptorSets.clear();
mDescriptorSetOffset = 0;
mDirtyTextures = false;
}
gl::LinkResult ProgramVk::load(const gl::Context *contextImpl,
......@@ -351,6 +354,11 @@ gl::Error ProgramVk::initDefaultUniformBlocks(const gl::Context *glContext)
ANGLE_TRY(updateDefaultUniformsDescriptorSet(contextVk));
}
else
{
// If the program has no uniforms, note this in the offset.
mDescriptorSetOffset = 1;
}
return gl::NoError();
}
......@@ -609,6 +617,60 @@ vk::Error ProgramVk::initPipelineLayout(ContextVk *context)
mDescriptorSetLayouts.push_back(std::move(uniformLayout));
}
const auto &samplerBindings = mState.getSamplerBindings();
if (!samplerBindings.empty())
{
std::vector<VkDescriptorSetLayoutBinding> textureBindings;
uint32_t textureCount = 0;
const auto &uniforms = mState.getUniforms();
for (unsigned int uniformIndex : mState.getSamplerUniformRange())
{
const gl::LinkedUniform &samplerUniform = uniforms[uniformIndex];
unsigned int samplerIndex = mState.getSamplerIndexFromUniformIndex(uniformIndex);
const gl::SamplerBinding &samplerBinding = samplerBindings[samplerIndex];
ASSERT(!samplerBinding.unreferenced);
VkDescriptorSetLayoutBinding layoutBinding;
uint32_t elementCount = samplerUniform.elementCount();
layoutBinding.binding = textureCount;
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
layoutBinding.descriptorCount = elementCount;
layoutBinding.stageFlags = 0;
if (samplerUniform.vertexStaticUse)
{
layoutBinding.stageFlags |= VK_SHADER_STAGE_VERTEX_BIT;
}
if (samplerUniform.fragmentStaticUse)
{
layoutBinding.stageFlags |= VK_SHADER_STAGE_FRAGMENT_BIT;
}
layoutBinding.pImmutableSamplers = nullptr;
textureCount += elementCount;
textureBindings.push_back(layoutBinding);
}
VkDescriptorSetLayoutCreateInfo textureInfo;
textureInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
textureInfo.pNext = nullptr;
textureInfo.flags = 0;
textureInfo.bindingCount = static_cast<uint32_t>(textureBindings.size());
textureInfo.pBindings = textureBindings.data();
vk::DescriptorSetLayout textureLayout;
ANGLE_TRY(textureLayout.init(device, textureInfo));
mDescriptorSetLayouts.push_back(std::move(textureLayout));
mDirtyTextures = true;
}
VkPipelineLayoutCreateInfo createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
createInfo.pNext = nullptr;
......@@ -625,7 +687,7 @@ vk::Error ProgramVk::initPipelineLayout(ContextVk *context)
vk::Error ProgramVk::initDescriptorSets(ContextVk *contextVk)
{
ASSERT(mDescriptorSet == VK_NULL_HANDLE);
ASSERT(mDescriptorSets.empty());
VkDevice device = contextVk->getDevice();
......@@ -633,16 +695,17 @@ vk::Error ProgramVk::initDescriptorSets(ContextVk *contextVk)
// 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();
uint32_t descriptorSetCount = static_cast<uint32_t>(mDescriptorSetLayouts.size());
// TODO(jmadill): Handle descriptor set layouts for textures.
allocInfo.descriptorSetCount = 1;
VkDescriptorSetAllocateInfo allocInfo;
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.pNext = nullptr;
allocInfo.descriptorPool = descriptorPool->getHandle();
allocInfo.descriptorSetCount = descriptorSetCount;
allocInfo.pSetLayouts = mDescriptorSetLayouts[0].ptr();
ANGLE_TRY(descriptorPool->allocateDescriptorSets(device, allocInfo, &mDescriptorSet));
mDescriptorSets.resize(descriptorSetCount, VK_NULL_HANDLE);
ANGLE_TRY(descriptorPool->allocateDescriptorSets(device, allocInfo, &mDescriptorSets[0]));
return vk::NoError();
}
......@@ -669,6 +732,8 @@ vk::Error ProgramVk::updateUniforms(ContextVk *contextVk)
return vk::NoError();
}
ASSERT(mDescriptorSetOffset == 0);
VkDevice device = contextVk->getDevice();
// Update buffer memory by immediate mapping. This immediate update only works once.
......@@ -712,7 +777,7 @@ vk::Error ProgramVk::updateDefaultUniformsDescriptorSet(ContextVk *contextVk)
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.pNext = nullptr;
writeInfo.dstSet = mDescriptorSet;
writeInfo.dstSet = mDescriptorSets[0];
writeInfo.dstBinding = bufferCount;
writeInfo.dstArrayElement = 0;
writeInfo.descriptorCount = 1;
......@@ -731,9 +796,82 @@ vk::Error ProgramVk::updateDefaultUniformsDescriptorSet(ContextVk *contextVk)
return vk::NoError();
}
VkDescriptorSet ProgramVk::getDescriptorSet() const
const std::vector<VkDescriptorSet> &ProgramVk::getDescriptorSets() const
{
return mDescriptorSets;
}
uint32_t ProgramVk::getDescriptorSetOffset() const
{
return mDescriptorSetOffset;
}
void ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk)
{
if (mState.getSamplerBindings().empty() || !mDirtyTextures)
{
return;
}
VkDescriptorSet descriptorSet = mDescriptorSets.back();
// TODO(jmadill): Don't hard-code the texture limit.
ShaderTextureArray<VkDescriptorImageInfo> descriptorImageInfo;
ShaderTextureArray<VkWriteDescriptorSet> writeDescriptorInfo;
uint32_t imageCount = 0;
const gl::State &glState = contextVk->getGLState();
const auto &completeTextures = glState.getCompleteTextureCache();
for (const auto &samplerBinding : mState.getSamplerBindings())
{
ASSERT(!samplerBinding.unreferenced);
// TODO(jmadill): Sampler arrays
ASSERT(samplerBinding.boundTextureUnits.size() == 1);
GLuint textureUnit = samplerBinding.boundTextureUnits[0];
const gl::Texture *texture = completeTextures[textureUnit];
// TODO(jmadill): Incomplete textures handling.
ASSERT(texture);
TextureVk *textureVk = GetImplAs<TextureVk>(texture);
const vk::Image &image = textureVk->getImage();
VkDescriptorImageInfo &imageInfo = descriptorImageInfo[imageCount];
imageInfo.sampler = textureVk->getSampler().getHandle();
imageInfo.imageView = textureVk->getImageView().getHandle();
imageInfo.imageLayout = image.getCurrentLayout();
auto &writeInfo = writeDescriptorInfo[imageCount];
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.pNext = nullptr;
writeInfo.dstSet = descriptorSet;
writeInfo.dstBinding = imageCount;
writeInfo.dstArrayElement = 0;
writeInfo.descriptorCount = 1;
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeInfo.pImageInfo = &imageInfo;
writeInfo.pBufferInfo = nullptr;
writeInfo.pTexelBufferView = nullptr;
imageCount++;
}
VkDevice device = contextVk->getDevice();
ASSERT(imageCount > 0);
vkUpdateDescriptorSets(device, imageCount, writeDescriptorInfo.data(), 0, nullptr);
mDirtyTextures = false;
}
void ProgramVk::invalidateTextures()
{
return mDescriptorSet;
mDirtyTextures = true;
}
} // namespace rx
......@@ -10,9 +10,12 @@
#ifndef LIBANGLE_RENDERER_VULKAN_PROGRAMVK_H_
#define LIBANGLE_RENDERER_VULKAN_PROGRAMVK_H_
#include "libANGLE/Constants.h"
#include "libANGLE/renderer/ProgramImpl.h"
#include "libANGLE/renderer/vulkan/renderervk_utils.h"
#include <array>
namespace rx
{
......@@ -114,7 +117,16 @@ class ProgramVk : public ProgramImpl
vk::Error updateUniforms(ContextVk *contextVk);
VkDescriptorSet getDescriptorSet() const;
const std::vector<VkDescriptorSet> &getDescriptorSets() const;
// In Vulkan, it is invalid to pass in a NULL descriptor set to vkCmdBindDescriptorSets.
// However, it's valid to leave them in an undefined, unbound state, if they are never used.
// This means when we want to ignore a descriptor set index, we need to pass in an offset
// parameter to BindDescriptorSets, which is an offset into the getDescriptorSets array.
uint32_t getDescriptorSetOffset() const;
void updateTexturesDescriptorSet(ContextVk *contextVk);
void invalidateTextures();
private:
void reset(VkDevice device);
......@@ -154,8 +166,13 @@ class ProgramVk : public ProgramImpl
// 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;
// Descriptor sets for uniform blocks and textures for this program.
std::vector<VkDescriptorSet> mDescriptorSets;
uint32_t mDescriptorSetOffset;
bool mDirtyTextures;
template <typename T>
using ShaderTextureArray = std::array<T, gl::IMPLEMENTATION_MAX_SHADER_TEXTURES>;
};
} // namespace rx
......
......@@ -34,6 +34,7 @@ gl::Error TextureVk::onDestroy(const gl::Context *context)
renderer->enqueueGarbageOrDeleteNow(*this, std::move(mImage));
renderer->enqueueGarbageOrDeleteNow(*this, std::move(mDeviceMemory));
renderer->enqueueGarbageOrDeleteNow(*this, std::move(mImageView));
renderer->enqueueGarbageOrDeleteNow(*this, std::move(mSampler));
return gl::NoError();
}
......@@ -77,7 +78,7 @@ gl::Error TextureVk::setImage(const gl::Context *context,
// TODO(jmadill): Are all these image transfer bits necessary?
imageInfo.usage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.queueFamilyIndexCount = 0;
imageInfo.pQueueFamilyIndices = nullptr;
......@@ -125,6 +126,30 @@ gl::Error TextureVk::setImage(const gl::Context *context,
ANGLE_TRY(mImageView.init(device, viewInfo));
// Create a simple sampler. Force basic parameter settings.
// TODO(jmadill): Sampler parameters.
VkSamplerCreateInfo samplerInfo;
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.pNext = nullptr;
samplerInfo.flags = 0;
samplerInfo.magFilter = VK_FILTER_NEAREST;
samplerInfo.minFilter = VK_FILTER_NEAREST;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.mipLodBias = 0.0f;
samplerInfo.anisotropyEnable = VK_FALSE;
samplerInfo.maxAnisotropy = 1.0f;
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 1.0f;
samplerInfo.borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
ANGLE_TRY(mSampler.init(device, samplerInfo));
// Handle initial data.
// TODO(jmadill): Consider re-using staging texture.
if (pixels)
......@@ -317,7 +342,7 @@ gl::Error TextureVk::getAttachmentRenderTarget(const gl::Context *context,
void TextureVk::syncState(const gl::Texture::DirtyBits &dirtyBits)
{
UNIMPLEMENTED();
// TODO(jmadill): Texture sync state.
}
gl::Error TextureVk::setStorageMultisample(const gl::Context *context,
......@@ -338,4 +363,22 @@ gl::Error TextureVk::initializeContents(const gl::Context *context,
return gl::NoError();
}
const vk::Image &TextureVk::getImage() const
{
ASSERT(mImage.valid());
return mImage;
}
const vk::ImageView &TextureVk::getImageView() const
{
ASSERT(mImageView.valid());
return mImageView;
}
const vk::Sampler &TextureVk::getSampler() const
{
ASSERT(mSampler.valid());
return mSampler;
}
} // namespace rx
......@@ -110,11 +110,16 @@ class TextureVk : public TextureImpl, public ResourceVk
gl::Error initializeContents(const gl::Context *context,
const gl::ImageIndex &imageIndex) override;
const vk::Image &getImage() const;
const vk::ImageView &getImageView() const;
const vk::Sampler &getSampler() const;
private:
// TODO(jmadill): support a more flexible storage back-end.
vk::Image mImage;
vk::DeviceMemory mDeviceMemory;
vk::ImageView mImageView;
vk::Sampler mSampler;
};
} // namespace rx
......
......@@ -951,6 +951,27 @@ Error DescriptorPool::allocateDescriptorSets(VkDevice device,
return NoError();
}
// Sampler implementation.
Sampler::Sampler()
{
}
void Sampler::destroy(VkDevice device)
{
if (valid())
{
vkDestroySampler(device, mHandle, nullptr);
mHandle = VK_NULL_HANDLE;
}
}
Error Sampler::init(VkDevice device, const VkSamplerCreateInfo &createInfo)
{
ASSERT(!valid());
ANGLE_VK_TRY(vkCreateSampler(device, &createInfo, nullptr, &mHandle));
return NoError();
}
// Fence implementation.
Fence::Fence()
{
......
......@@ -457,6 +457,14 @@ class DescriptorPool final : public WrappedObject<DescriptorPool, VkDescriptorPo
VkDescriptorSet *descriptorSetsOut);
};
class Sampler final : public WrappedObject<Sampler, VkSampler>
{
public:
Sampler();
void destroy(VkDevice device);
Error init(VkDevice device, const VkSamplerCreateInfo &createInfo);
};
class Fence final : public WrappedObject<Fence, VkFence>
{
public:
......
......@@ -470,6 +470,30 @@ TEST_P(SimpleOperationTest, LinkProgramWithTexture)
EXPECT_GL_NO_ERROR();
}
// Creates a program with a texture and renders with it.
TEST_P(SimpleOperationTest, DrawWithTexture)
{
std::array<GLColor, 4> colors = {
{GLColor::red, GLColor::green, GLColor::blue, GLColor::yellow}};
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, colors.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
draw2DTexturedQuad(0.5f, 1.0f, true);
EXPECT_GL_NO_ERROR();
int w = getWindowWidth() - 2;
int h = getWindowHeight() - 2;
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(w, 0, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(0, h, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(w, h, GLColor::yellow);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
ANGLE_INSTANTIATE_TEST(SimpleOperationTest,
ES2_D3D9(),
......
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