Commit 035fd6b3 by Jamie Madill Committed by Commit Bot

Vulkan: Implement very basic textures.

This is a quick implementation which supports only one backing Image and one type of ImageView at a time, for 2D texture only. It also implements a helper class for finding compatible memory pools. It's possible we can keep a cache of memory pool indexes given the guarantees the Vulkan spec has on compatible memory types (see the documentation for VkMemoryRequirements). BUG=angleproject:2167 Change-Id: I1d7a8eaec90f240273ad75194e23430d6d4c5dc1 Reviewed-on: https://chromium-review.googlesource.com/680000 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarFrank Henigman <fjhenigman@chromium.org>
parent 488130e0
......@@ -27,9 +27,10 @@ BufferVk::~BufferVk()
void BufferVk::destroy(const gl::Context *context)
{
VkDevice device = GetImplAs<ContextVk>(context)->getDevice();
ContextVk *contextVk = GetImplAs<ContextVk>(context);
RendererVk *renderer = contextVk->getRenderer();
mBuffer.destroy(device);
renderer->enqueueGarbageOrDeleteNow(*this, std::move(mBuffer));
}
gl::Error BufferVk::setData(const gl::Context *context,
......
......@@ -283,7 +283,8 @@ gl::Error FramebufferVk::readPixels(const gl::Context *context,
vk::Image *readImage = renderTarget->image;
vk::StagingImage stagingImage;
ANGLE_TRY(renderer->createStagingImage(TextureDimension::TEX_2D, *renderTarget->format,
renderTarget->extents, &stagingImage));
renderTarget->extents, vk::StagingUsage::Read,
&stagingImage));
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(contextVk->getStartedCommandBuffer(&commandBuffer));
......@@ -332,10 +333,6 @@ gl::Error FramebufferVk::readPixels(const gl::Context *context,
stagingImage.getDeviceMemory().unmap(device);
renderer->enqueueGarbage(renderer->getCurrentQueueSerial(), std::move(stagingImage));
stagingImage.getImage().destroy(renderer->getDevice());
stagingImage.destroy(device);
return vk::NoError();
}
......
......@@ -93,7 +93,6 @@ RendererVk::RendererVk()
mQueue(VK_NULL_HANDLE),
mCurrentQueueFamilyIndex(std::numeric_limits<uint32_t>::max()),
mDevice(VK_NULL_HANDLE),
mHostVisibleMemoryIndex(std::numeric_limits<uint32_t>::max()),
mGlslangWrapper(nullptr),
mLastCompletedQueueSerial(mQueueSerialFactory.generate()),
mCurrentQueueSerial(mQueueSerialFactory.generate()),
......@@ -338,21 +337,8 @@ vk::Error RendererVk::initialize(const egl::AttributeMap &attribs, const char *w
ANGLE_TRY(initializeDevice(firstGraphicsQueueFamily));
}
VkPhysicalDeviceMemoryProperties memoryProperties;
vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &memoryProperties);
for (uint32_t memoryIndex = 0; memoryIndex < memoryProperties.memoryTypeCount; ++memoryIndex)
{
if ((memoryProperties.memoryTypes[memoryIndex].propertyFlags &
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
{
mHostVisibleMemoryIndex = memoryIndex;
break;
}
}
ANGLE_VK_CHECK(mHostVisibleMemoryIndex < std::numeric_limits<uint32_t>::max(),
VK_ERROR_INITIALIZATION_FAILED);
// Store the physical device memory properties so we can find the right memory pools.
mMemoryProperties.init(mPhysicalDevice);
mGlslangWrapper = GlslangWrapper::GetReference();
......@@ -540,7 +526,10 @@ void RendererVk::generateCaps(gl::Caps *outCaps,
outCaps->maxDrawBuffers = 1;
outCaps->maxVertexAttributes = gl::MAX_VERTEX_ATTRIBS;
outCaps->maxVertexAttribBindings = gl::MAX_VERTEX_ATTRIB_BINDINGS;
outCaps->maxVaryingVectors = 16;
outCaps->maxVaryingVectors = 16;
outCaps->maxTextureImageUnits = 1;
outCaps->maxCombinedTextureImageUnits = 1;
outCaps->max2DTextureSize = 1024;
// Enable this for simple buffer readback testing, but some functionality is missing.
// TODO(jmadill): Support full mapBufferRange extension.
......@@ -774,13 +763,11 @@ vk::Error RendererVk::submitFrame(const VkSubmitInfo &submitInfo)
vk::Error RendererVk::createStagingImage(TextureDimension dimension,
const vk::Format &format,
const gl::Extents &extent,
vk::StagingUsage usage,
vk::StagingImage *imageOut)
{
ASSERT(mHostVisibleMemoryIndex != std::numeric_limits<uint32_t>::max());
ANGLE_TRY(imageOut->init(mDevice, mCurrentQueueFamilyIndex, mHostVisibleMemoryIndex, dimension,
format.native, extent));
ANGLE_TRY(imageOut->init(mDevice, mCurrentQueueFamilyIndex, mMemoryProperties, dimension,
format.native, extent, usage));
return vk::NoError();
}
......
......@@ -66,6 +66,7 @@ class RendererVk : angle::NonCopyable
vk::Error createStagingImage(TextureDimension dimension,
const vk::Format &format,
const gl::Extents &extent,
vk::StagingUsage usage,
vk::StagingImage *imageOut);
GlslangWrapper *getGlslangWrapper();
......@@ -80,18 +81,22 @@ class RendererVk : angle::NonCopyable
}
template <typename T>
void enqueueGarbageOrDeleteNow(const ResourceVk &resouce, T &&object)
void enqueueGarbageOrDeleteNow(const ResourceVk &resource, T &&object)
{
if (resouce.getDeleteSchedule(mLastCompletedQueueSerial) == DeleteSchedule::NOW)
if (resource.getDeleteSchedule(mLastCompletedQueueSerial) == DeleteSchedule::NOW)
{
object.destroy(mDevice);
}
else
{
enqueueGarbage(resouce.getStoredQueueSerial(), std::move(object));
enqueueGarbage(resource.getStoredQueueSerial(), std::move(object));
}
}
uint32_t getQueueFamilyIndex() const { return mCurrentQueueFamilyIndex; }
const vk::MemoryProperties &getMemoryProperties() const { return mMemoryProperties; }
private:
void ensureCapsInitialized() const;
void generateCaps(gl::Caps *outCaps,
......@@ -122,7 +127,6 @@ class RendererVk : angle::NonCopyable
VkDevice mDevice;
vk::CommandPool mCommandPool;
vk::CommandBuffer mCommandBuffer;
uint32_t mHostVisibleMemoryIndex;
GlslangWrapper *mGlslangWrapper;
SerialFactory mQueueSerialFactory;
Serial mLastCompletedQueueSerial;
......@@ -130,6 +134,7 @@ class RendererVk : angle::NonCopyable
std::vector<vk::CommandBufferAndSerial> mInFlightCommands;
std::vector<vk::FenceAndSerial> mInFlightFences;
std::vector<std::unique_ptr<vk::IGarbageObject>> mGarbage;
vk::MemoryProperties mMemoryProperties;
};
} // namespace rx
......
......@@ -10,6 +10,10 @@
#include "libANGLE/renderer/vulkan/TextureVk.h"
#include "common/debug.h"
#include "libANGLE/Context.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/RendererVk.h"
#include "libANGLE/renderer/vulkan/formatutilsvk.h"
namespace rx
{
......@@ -22,6 +26,18 @@ TextureVk::~TextureVk()
{
}
gl::Error TextureVk::onDestroy(const gl::Context *context)
{
ContextVk *contextVk = GetImplAs<ContextVk>(context);
RendererVk *renderer = contextVk->getRenderer();
renderer->enqueueGarbageOrDeleteNow(*this, std::move(mImage));
renderer->enqueueGarbageOrDeleteNow(*this, std::move(mDeviceMemory));
renderer->enqueueGarbageOrDeleteNow(*this, std::move(mImageView));
return gl::NoError();
}
gl::Error TextureVk::setImage(const gl::Context *context,
GLenum target,
size_t level,
......@@ -32,8 +48,140 @@ gl::Error TextureVk::setImage(const gl::Context *context,
const gl::PixelUnpackState &unpack,
const uint8_t *pixels)
{
UNIMPLEMENTED();
return gl::InternalError();
// TODO(jmadill): support multi-level textures.
ASSERT(level == 0);
// TODO(jmadill): support texture re-definition.
ASSERT(!mImage.valid());
// TODO(jmadill): support other types of textures.
ASSERT(target == GL_TEXTURE_2D);
// Convert internalFormat to sized internal format.
const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat, type);
const vk::Format &vkFormat = vk::Format::Get(formatInfo.sizedInternalFormat);
VkImageCreateInfo imageInfo;
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.pNext = nullptr;
imageInfo.flags = 0;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.format = vkFormat.native;
imageInfo.extent.width = size.width;
imageInfo.extent.height = size.height;
imageInfo.extent.depth = size.depth;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
// 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);
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.queueFamilyIndexCount = 0;
imageInfo.pQueueFamilyIndices = nullptr;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
ContextVk *contextVk = GetImplAs<ContextVk>(context);
VkDevice device = contextVk->getDevice();
ANGLE_TRY(mImage.init(device, imageInfo));
// Allocate the device memory for the image.
// TODO(jmadill): Use more intelligent device memory allocation.
VkMemoryRequirements memoryRequirements;
mImage.getMemoryRequirements(device, &memoryRequirements);
RendererVk *renderer = contextVk->getRenderer();
uint32_t memoryIndex = renderer->getMemoryProperties().findCompatibleMemoryIndex(
memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VkMemoryAllocateInfo allocateInfo;
allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocateInfo.pNext = nullptr;
allocateInfo.allocationSize = memoryRequirements.size;
allocateInfo.memoryTypeIndex = memoryIndex;
ANGLE_TRY(mDeviceMemory.allocate(device, allocateInfo));
ANGLE_TRY(mImage.bindMemory(device, mDeviceMemory));
VkImageViewCreateInfo viewInfo;
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.pNext = nullptr;
viewInfo.flags = 0;
viewInfo.image = mImage.getHandle();
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = vkFormat.native;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
ANGLE_TRY(mImageView.init(device, viewInfo));
// Handle initial data.
// TODO(jmadill): Consider re-using staging texture.
if (pixels)
{
vk::StagingImage stagingImage;
ANGLE_TRY(renderer->createStagingImage(TextureDimension::TEX_2D, vkFormat, size,
vk::StagingUsage::Write, &stagingImage));
GLuint inputRowPitch = 0;
ANGLE_TRY_RESULT(
formatInfo.computeRowPitch(type, size.width, unpack.alignment, unpack.rowLength),
inputRowPitch);
GLuint inputDepthPitch = 0;
ANGLE_TRY_RESULT(
formatInfo.computeDepthPitch(size.height, unpack.imageHeight, inputRowPitch),
inputDepthPitch);
// TODO(jmadill): skip images for 3D Textures.
bool applySkipImages = false;
GLuint inputSkipBytes = 0;
ANGLE_TRY_RESULT(
formatInfo.computeSkipBytes(inputRowPitch, inputDepthPitch, unpack, applySkipImages),
inputSkipBytes);
auto loadFunction = vkFormat.getLoadFunctions()(type);
uint8_t *mapPointer = nullptr;
ANGLE_TRY(
stagingImage.getDeviceMemory().map(device, 0, stagingImage.getSize(), 0, &mapPointer));
const uint8_t *source = pixels + inputSkipBytes;
loadFunction.loadFunction(size.width, size.height, size.depth, source, inputRowPitch,
inputDepthPitch, mapPointer, inputRowPitch, inputDepthPitch);
stagingImage.getDeviceMemory().unmap(device);
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(contextVk->getStartedCommandBuffer(&commandBuffer));
setQueueSerial(renderer->getCurrentQueueSerial());
stagingImage.getImage().changeLayoutTop(
VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, commandBuffer);
mImage.changeLayoutTop(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
commandBuffer);
gl::Box wholeRegion(0, 0, 0, size.width, size.height, size.depth);
commandBuffer->copySingleImage(stagingImage.getImage(), mImage, wholeRegion,
VK_IMAGE_ASPECT_COLOR_BIT);
// TODO(jmadill): Re-use staging images.
renderer->enqueueGarbage(renderer->getCurrentQueueSerial(), std::move(stagingImage));
}
return gl::NoError();
}
gl::Error TextureVk::setSubImage(const gl::Context *context,
......
......@@ -11,15 +11,17 @@
#define LIBANGLE_RENDERER_VULKAN_TEXTUREVK_H_
#include "libANGLE/renderer/TextureImpl.h"
#include "libANGLE/renderer/vulkan/renderervk_utils.h"
namespace rx
{
class TextureVk : public TextureImpl
class TextureVk : public TextureImpl, public ResourceVk
{
public:
TextureVk(const gl::TextureState &state);
~TextureVk() override;
gl::Error onDestroy(const gl::Context *context) override;
gl::Error setImage(const gl::Context *context,
GLenum target,
......@@ -107,6 +109,12 @@ class TextureVk : public TextureImpl
gl::Error initializeContents(const gl::Context *context,
const gl::ImageIndex &imageIndex) override;
private:
// TODO(jmadill): support a more flexible storage back-end.
vk::Image mImage;
vk::DeviceMemory mDeviceMemory;
vk::ImageView mImageView;
};
} // namespace rx
......
......@@ -62,6 +62,7 @@ VkAccessFlags GetBasicLayoutAccessFlags(VkImageLayout layout)
return VK_ACCESS_TRANSFER_READ_BIT;
case VK_IMAGE_LAYOUT_UNDEFINED:
case VK_IMAGE_LAYOUT_GENERAL:
case VK_IMAGE_LAYOUT_PREINITIALIZED:
return 0;
default:
// TODO(jmadill): Investigate other flags.
......@@ -69,6 +70,23 @@ VkAccessFlags GetBasicLayoutAccessFlags(VkImageLayout layout)
return 0;
}
}
VkImageUsageFlags GetImageUsageFlags(vk::StagingUsage usage)
{
switch (usage)
{
case vk::StagingUsage::Read:
return VK_IMAGE_USAGE_TRANSFER_DST_BIT;
case vk::StagingUsage::Write:
return VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
case vk::StagingUsage::Both:
return (VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
default:
UNREACHABLE();
return 0;
}
}
} // anonymous namespace
// Mirrors std_validation_str in loader.h
......@@ -459,6 +477,7 @@ Error Image::init(VkDevice device, const VkImageCreateInfo &createInfo)
{
ASSERT(!valid());
ANGLE_VK_TRY(vkCreateImage(device, &createInfo, nullptr, &mHandle));
mCurrentLayout = createInfo.initialLayout;
return NoError();
}
......@@ -697,10 +716,11 @@ void StagingImage::retain(VkDevice device, StagingImage &&other)
Error StagingImage::init(VkDevice device,
uint32_t queueFamilyIndex,
uint32_t hostVisibleMemoryIndex,
const vk::MemoryProperties &memoryProperties,
TextureDimension dimension,
VkFormat format,
const gl::Extents &extent)
const gl::Extents &extent,
StagingUsage usage)
{
VkImageCreateInfo createInfo;
......@@ -716,26 +736,30 @@ Error StagingImage::init(VkDevice device,
createInfo.arrayLayers = 1;
createInfo.samples = VK_SAMPLE_COUNT_1_BIT;
createInfo.tiling = VK_IMAGE_TILING_LINEAR;
createInfo.usage = (VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
createInfo.usage = GetImageUsageFlags(usage);
createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 1;
createInfo.pQueueFamilyIndices = &queueFamilyIndex;
createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
// Use Preinitialized for writable staging images - in these cases we want to map the memory
// before we do a copy. For readback images, use an undefined layout.
createInfo.initialLayout = usage == vk::StagingUsage::Read ? VK_IMAGE_LAYOUT_UNDEFINED
: VK_IMAGE_LAYOUT_PREINITIALIZED;
ANGLE_TRY(mImage.init(device, createInfo));
VkMemoryRequirements memoryRequirements;
mImage.getMemoryRequirements(device, &memoryRequirements);
// Ensure we can read this memory.
ANGLE_VK_CHECK((memoryRequirements.memoryTypeBits & (1 << hostVisibleMemoryIndex)) != 0,
VK_ERROR_VALIDATION_FAILED_EXT);
// Find the right kind of memory index.
uint32_t memoryIndex = memoryProperties.findCompatibleMemoryIndex(
memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
VkMemoryAllocateInfo allocateInfo;
allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocateInfo.pNext = nullptr;
allocateInfo.allocationSize = memoryRequirements.size;
allocateInfo.memoryTypeIndex = hostVisibleMemoryIndex;
allocateInfo.memoryTypeIndex = memoryIndex;
ANGLE_TRY(mDeviceMemory.allocate(device, allocateInfo));
ANGLE_TRY(mImage.bindMemory(device, mDeviceMemory));
......@@ -871,6 +895,38 @@ VkResult Fence::getStatus(VkDevice device) const
return vkGetFenceStatus(device, mHandle);
}
// MemoryProperties implementation.
MemoryProperties::MemoryProperties() : mMemoryProperties{0}
{
}
void MemoryProperties::init(VkPhysicalDevice physicalDevice)
{
ASSERT(mMemoryProperties.memoryTypeCount == 0);
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &mMemoryProperties);
ASSERT(mMemoryProperties.memoryTypeCount > 0);
}
uint32_t MemoryProperties::findCompatibleMemoryIndex(uint32_t bitMask, uint32_t propertyFlags) const
{
ASSERT(mMemoryProperties.memoryTypeCount > 0);
// TODO(jmadill): Cache compatible memory indexes after finding them once.
for (size_t memoryIndex : angle::BitSet32<32>(bitMask))
{
ASSERT(memoryIndex < mMemoryProperties.memoryTypeCount);
if ((mMemoryProperties.memoryTypes[memoryIndex].propertyFlags & propertyFlags) ==
propertyFlags)
{
return static_cast<uint32_t>(memoryIndex);
}
}
UNREACHABLE();
return std::numeric_limits<uint32_t>::max();
}
} // namespace vk
Optional<uint32_t> FindMemoryType(const VkPhysicalDeviceMemoryProperties &memoryProps,
......
......@@ -89,6 +89,18 @@ class Image;
class Pipeline;
class RenderPass;
class MemoryProperties final : angle::NonCopyable
{
public:
MemoryProperties();
void init(VkPhysicalDevice physicalDevice);
uint32_t findCompatibleMemoryIndex(uint32_t bitMask, uint32_t propertyFlags) const;
private:
VkPhysicalDeviceMemoryProperties mMemoryProperties;
};
class Error final
{
public:
......@@ -321,6 +333,13 @@ class RenderPass final : public WrappedObject<RenderPass, VkRenderPass>
Error init(VkDevice device, const VkRenderPassCreateInfo &createInfo);
};
enum class StagingUsage
{
Read,
Write,
Both,
};
class StagingImage final : angle::NonCopyable
{
public:
......@@ -331,10 +350,11 @@ class StagingImage final : angle::NonCopyable
vk::Error init(VkDevice device,
uint32_t queueFamilyIndex,
uint32_t hostVisibleMemoryIndex,
const MemoryProperties &memoryProperties,
TextureDimension dimension,
VkFormat format,
const gl::Extents &extent);
const gl::Extents &extent,
StagingUsage usage);
Image &getImage() { return mImage; }
const Image &getImage() const { return mImage; }
......
......@@ -330,6 +330,26 @@ void main()
EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::yellow);
}
// Creates a texture, no other operations.
TEST_P(SimpleOperationTest, CreateTexture2DNoData)
{
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
ASSERT_GL_NO_ERROR();
}
// Creates a texture, no other operations.
TEST_P(SimpleOperationTest, CreateTexture2DWithData)
{
std::vector<GLColor> colors(16 * 16, GLColor::red);
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, colors.data());
ASSERT_GL_NO_ERROR();
}
// 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