Commit ce4918f1 by Jamie Madill Committed by Commit Bot

Vulkan: Sanitize Images & Buffers with non-zero values.

Only enabled for specific tests at the moment. This CL allows our tests to sanitizes memory for the robust resource access extension. It is quite slow so should not be enabled by default. Only works for 1 level 2D color textures and buffers. Makes several flaky robust resource initialization tests consistently fail. Controlled via an angle::Feature in FeaturesVk. It works by initializing memory to an abitrary non-zero value: - if newly allocated memory is mappable, we map it in init and set it - if a buffer or texture can be a transfer destination, we use a staging resource - otherwise we don't attempt to initialize the resource. Bug: angleproject:4384 Change-Id: I9b4f347bfcddf3096f491ed0243bef86837feaa0 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2043271 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCody Northrop <cnorthrop@google.com>
parent dec00bc8
...@@ -235,6 +235,13 @@ struct FeaturesVk : FeatureSetBase ...@@ -235,6 +235,13 @@ struct FeaturesVk : FeatureSetBase
Feature supportsExternalMemoryHost = { Feature supportsExternalMemoryHost = {
"supports_external_memory_host", FeatureCategory::VulkanFeatures, "supports_external_memory_host", FeatureCategory::VulkanFeatures,
"VkDevice supports the VK_EXT_external_memory_host extension", &members}; "VkDevice supports the VK_EXT_external_memory_host extension", &members};
// Whether to fill new buffers and textures with nonzero data to sanitize robust resource
// initialization and flush out assumptions about zero init.
Feature allocateNonZeroMemory = {
"allocate_non_zero_memory", FeatureCategory::VulkanFeatures,
"Fill new allocations with non-zero values to flush out errors.", &members,
"http://anglebug.com/4384"};
}; };
inline FeaturesVk::FeaturesVk() = default; inline FeaturesVk::FeaturesVk() = default;
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#define LIBANGLE_RENDERER_SERIAL_UTILS_H_ #define LIBANGLE_RENDERER_SERIAL_UTILS_H_
#include <atomic> #include <atomic>
#include <limits>
#include "common/angleutils.h" #include "common/angleutils.h"
#include "common/debug.h" #include "common/debug.h"
...@@ -45,6 +46,8 @@ class Serial final ...@@ -45,6 +46,8 @@ class Serial final
constexpr Serial(const Serial &other) = default; constexpr Serial(const Serial &other) = default;
Serial &operator=(const Serial &other) = default; Serial &operator=(const Serial &other) = default;
static constexpr Serial Infinite() { return Serial(std::numeric_limits<uint64_t>::max()); }
constexpr bool operator==(const Serial &other) const constexpr bool operator==(const Serial &other) const
{ {
return mValue != kInvalid && mValue == other.mValue; return mValue != kInvalid && mValue == other.mValue;
......
...@@ -322,6 +322,9 @@ class SharedResourceUse final : angle::NonCopyable ...@@ -322,6 +322,9 @@ class SharedResourceUse final : angle::NonCopyable
mUse->counter++; mUse->counter++;
} }
// Specifically for use with command buffers that are used as one-offs.
void updateSerialOneOff(Serial serial) { mUse->serial = serial; }
ANGLE_INLINE void release() ANGLE_INLINE void release()
{ {
ASSERT(valid()); ASSERT(valid());
......
...@@ -468,7 +468,7 @@ angle::Result CommandQueue::submitFrame(vk::Context *context, ...@@ -468,7 +468,7 @@ angle::Result CommandQueue::submitFrame(vk::Context *context,
batch.fence.copy(device, sharedFence); batch.fence.copy(device, sharedFence);
ANGLE_TRY( ANGLE_TRY(
renderer->queueSubmit(context, priority, submitInfo, batch.fence.get(), &batch.serial)); renderer->queueSubmit(context, priority, submitInfo, &batch.fence.get(), &batch.serial));
if (!currentGarbage->empty()) if (!currentGarbage->empty())
{ {
...@@ -3941,7 +3941,7 @@ angle::Result ContextVk::getTimestamp(uint64_t *timestampOut) ...@@ -3941,7 +3941,7 @@ angle::Result ContextVk::getTimestamp(uint64_t *timestampOut)
Serial throwAwaySerial; Serial throwAwaySerial;
ANGLE_TRY( ANGLE_TRY(
mRenderer->queueSubmit(this, mContextPriority, submitInfo, fence.get(), &throwAwaySerial)); mRenderer->queueSubmit(this, mContextPriority, submitInfo, &fence.get(), &throwAwaySerial));
// Wait for the submission to finish. Given no semaphores, there is hope that it would execute // Wait for the submission to finish. Given no semaphores, there is hope that it would execute
// in parallel with what's already running on the GPU. // in parallel with what's already running on the GPU.
......
...@@ -203,7 +203,8 @@ void DisplayVk::generateExtensions(egl::DisplayExtensions *outExtensions) const ...@@ -203,7 +203,8 @@ void DisplayVk::generateExtensions(egl::DisplayExtensions *outExtensions) const
outExtensions->framebufferTargetANDROID = true; outExtensions->framebufferTargetANDROID = true;
#endif // defined(ANGLE_PLATFORM_ANDROID) #endif // defined(ANGLE_PLATFORM_ANDROID)
outExtensions->contextPriority = true; // Disable context priority when non-zero memory init is enabled. This enforces a queue order.
outExtensions->contextPriority = !getRenderer()->getFeatures().allocateNonZeroMemory.enabled;
outExtensions->noConfigContext = true; outExtensions->noConfigContext = true;
#if defined(ANGLE_PLATFORM_GGP) #if defined(ANGLE_PLATFORM_GGP)
......
...@@ -593,9 +593,27 @@ RendererVk::~RendererVk() ...@@ -593,9 +593,27 @@ RendererVk::~RendererVk()
void RendererVk::onDestroy(vk::Context *context) void RendererVk::onDestroy(vk::Context *context)
{ {
// Force all commands to finish by flushing all queues.
for (VkQueue queue : mQueues)
{
if (queue != VK_NULL_HANDLE)
{
vkQueueWaitIdle(queue);
}
}
// Then assign an infinite "last completed" serial to force garbage to delete.
mLastCompletedQueueSerial = Serial::Infinite();
(void)cleanupGarbage(context, true); (void)cleanupGarbage(context, true);
ASSERT(mSharedGarbage.empty()); ASSERT(mSharedGarbage.empty());
for (PendingOneOffCommands &pending : mPendingOneOffCommands)
{
pending.commandBuffer.releaseHandle();
}
mOneOffCommandPool.destroy(mDevice);
mFenceRecycler.destroy(mDevice); mFenceRecycler.destroy(mDevice);
mPipelineLayoutCache.destroy(mDevice); mPipelineLayoutCache.destroy(mDevice);
...@@ -1597,6 +1615,10 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev ...@@ -1597,6 +1615,10 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev
ANGLE_FEATURE_CONDITION((&mFeatures), commandGraph, false); ANGLE_FEATURE_CONDITION((&mFeatures), commandGraph, false);
// Allocation sanitization disabled by default because of a heaveyweight implementation
// that can cause OOM and timeouts.
ANGLE_FEATURE_CONDITION((&mFeatures), allocateNonZeroMemory, false);
ANGLE_FEATURE_CONDITION( ANGLE_FEATURE_CONDITION(
(&mFeatures), supportsExternalMemoryHost, (&mFeatures), supportsExternalMemoryHost,
ExtensionFound(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME, deviceExtensionNames)); ExtensionFound(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME, deviceExtensionNames));
...@@ -1828,12 +1850,13 @@ bool RendererVk::hasBufferFormatFeatureBits(VkFormat format, const VkFormatFeatu ...@@ -1828,12 +1850,13 @@ bool RendererVk::hasBufferFormatFeatureBits(VkFormat format, const VkFormatFeatu
angle::Result RendererVk::queueSubmit(vk::Context *context, angle::Result RendererVk::queueSubmit(vk::Context *context,
egl::ContextPriority priority, egl::ContextPriority priority,
const VkSubmitInfo &submitInfo, const VkSubmitInfo &submitInfo,
const vk::Fence &fence, const vk::Fence *fence,
Serial *serialOut) Serial *serialOut)
{ {
{ {
std::lock_guard<decltype(mQueueMutex)> lock(mQueueMutex); std::lock_guard<decltype(mQueueMutex)> lock(mQueueMutex);
ANGLE_VK_TRY(context, vkQueueSubmit(mQueues[priority], 1, &submitInfo, fence.getHandle())); VkFence handle = fence ? fence->getHandle() : VK_NULL_HANDLE;
ANGLE_VK_TRY(context, vkQueueSubmit(mQueues[priority], 1, &submitInfo, handle));
} }
ANGLE_TRY(cleanupGarbage(context, false)); ANGLE_TRY(cleanupGarbage(context, false));
...@@ -1845,6 +1868,23 @@ angle::Result RendererVk::queueSubmit(vk::Context *context, ...@@ -1845,6 +1868,23 @@ angle::Result RendererVk::queueSubmit(vk::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result RendererVk::queueSubmitOneOff(vk::Context *context,
vk::PrimaryCommandBuffer &&primary,
egl::ContextPriority priority,
Serial *serialOut)
{
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = primary.ptr();
ANGLE_TRY(queueSubmit(context, priority, submitInfo, nullptr, serialOut));
mPendingOneOffCommands.push_back({*serialOut, std::move(primary)});
return angle::Result::Continue;
}
angle::Result RendererVk::queueWaitIdle(vk::Context *context, egl::ContextPriority priority) angle::Result RendererVk::queueWaitIdle(vk::Context *context, egl::ContextPriority priority)
{ {
{ {
...@@ -2000,4 +2040,42 @@ void RendererVk::reloadVolkIfNeeded() const ...@@ -2000,4 +2040,42 @@ void RendererVk::reloadVolkIfNeeded() const
volkLoadDevice(mDevice); volkLoadDevice(mDevice);
} }
} }
angle::Result RendererVk::getCommandBufferOneOff(vk::Context *context,
vk::PrimaryCommandBuffer *commandBufferOut)
{
if (!mOneOffCommandPool.valid())
{
VkCommandPoolCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
createInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
ANGLE_VK_TRY(context, mOneOffCommandPool.init(mDevice, createInfo));
}
if (!mPendingOneOffCommands.empty() &&
mPendingOneOffCommands.front().serial < mLastCompletedQueueSerial)
{
*commandBufferOut = std::move(mPendingOneOffCommands.front().commandBuffer);
mPendingOneOffCommands.pop_front();
ANGLE_VK_TRY(context, commandBufferOut->reset());
}
else
{
VkCommandBufferAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = 1;
allocInfo.commandPool = mOneOffCommandPool.getHandle();
ANGLE_VK_TRY(context, commandBufferOut->init(context->getDevice(), allocInfo));
}
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = 0;
beginInfo.pInheritanceInfo = nullptr;
ANGLE_VK_TRY(context, commandBufferOut->begin(beginInfo));
return angle::Result::Continue;
}
} // namespace rx } // namespace rx
...@@ -10,8 +10,10 @@ ...@@ -10,8 +10,10 @@
#ifndef LIBANGLE_RENDERER_VULKAN_RENDERERVK_H_ #ifndef LIBANGLE_RENDERER_VULKAN_RENDERERVK_H_
#define LIBANGLE_RENDERER_VULKAN_RENDERERVK_H_ #define LIBANGLE_RENDERER_VULKAN_RENDERERVK_H_
#include <deque>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include "vk_ext_provoking_vertex.h" #include "vk_ext_provoking_vertex.h"
#include "volk.h" #include "volk.h"
...@@ -169,12 +171,23 @@ class RendererVk : angle::NonCopyable ...@@ -169,12 +171,23 @@ class RendererVk : angle::NonCopyable
angle::Result queueSubmit(vk::Context *context, angle::Result queueSubmit(vk::Context *context,
egl::ContextPriority priority, egl::ContextPriority priority,
const VkSubmitInfo &submitInfo, const VkSubmitInfo &submitInfo,
const vk::Fence &fence, const vk::Fence *fence,
Serial *serialOut); Serial *serialOut);
angle::Result queueWaitIdle(vk::Context *context, egl::ContextPriority priority); angle::Result queueWaitIdle(vk::Context *context, egl::ContextPriority priority);
angle::Result deviceWaitIdle(vk::Context *context); angle::Result deviceWaitIdle(vk::Context *context);
VkResult queuePresent(egl::ContextPriority priority, const VkPresentInfoKHR &presentInfo); VkResult queuePresent(egl::ContextPriority priority, const VkPresentInfoKHR &presentInfo);
// This command buffer should be submitted immediately via queueSubmitOneOff.
angle::Result getCommandBufferOneOff(vk::Context *context,
vk::PrimaryCommandBuffer *commandBufferOut);
// Fire off a single command buffer immediately with default priority.
// Command buffer must be allocated with getCommandBufferOneOff and is reclaimed.
angle::Result queueSubmitOneOff(vk::Context *context,
vk::PrimaryCommandBuffer &&primary,
egl::ContextPriority priority,
Serial *serialOut);
angle::Result newSharedFence(vk::Context *context, vk::Shared<vk::Fence> *sharedFenceOut); angle::Result newSharedFence(vk::Context *context, vk::Shared<vk::Fence> *sharedFenceOut);
inline void resetSharedFence(vk::Shared<vk::Fence> *sharedFenceIn) inline void resetSharedFence(vk::Shared<vk::Fence> *sharedFenceIn)
{ {
...@@ -201,8 +214,11 @@ class RendererVk : angle::NonCopyable ...@@ -201,8 +214,11 @@ class RendererVk : angle::NonCopyable
void collectGarbage(vk::SharedResourceUse &&use, std::vector<vk::GarbageObject> &&sharedGarbage) void collectGarbage(vk::SharedResourceUse &&use, std::vector<vk::GarbageObject> &&sharedGarbage)
{ {
if (!sharedGarbage.empty())
{
mSharedGarbage.emplace_back(std::move(use), std::move(sharedGarbage)); mSharedGarbage.emplace_back(std::move(use), std::move(sharedGarbage));
} }
}
static constexpr size_t kMaxExtensionNames = 200; static constexpr size_t kMaxExtensionNames = 200;
using ExtensionNameList = angle::FixedVector<const char *, kMaxExtensionNames>; using ExtensionNameList = angle::FixedVector<const char *, kMaxExtensionNames>;
...@@ -326,6 +342,16 @@ class RendererVk : angle::NonCopyable ...@@ -326,6 +342,16 @@ class RendererVk : angle::NonCopyable
static constexpr double kPercentMaxMemoryAllocationCount = 0.3; static constexpr double kPercentMaxMemoryAllocationCount = 0.3;
// How many objects to garbage collect before issuing a flush() // How many objects to garbage collect before issuing a flush()
uint32_t mGarbageCollectionFlushThreshold; uint32_t mGarbageCollectionFlushThreshold;
// Only used for "one off" command buffers.
vk::CommandPool mOneOffCommandPool;
struct PendingOneOffCommands
{
Serial serial;
vk::PrimaryCommandBuffer commandBuffer;
};
std::deque<PendingOneOffCommands> mPendingOneOffCommands;
}; };
} // namespace rx } // namespace rx
......
...@@ -201,13 +201,10 @@ angle::Result OffscreenSurfaceVk::AttachmentImage::initializeWithExternalMemory( ...@@ -201,13 +201,10 @@ angle::Result OffscreenSurfaceVk::AttachmentImage::initializeWithExternalMemory(
void OffscreenSurfaceVk::AttachmentImage::destroy(const egl::Display *display) void OffscreenSurfaceVk::AttachmentImage::destroy(const egl::Display *display)
{ {
DisplayVk *displayVk = vk::GetImpl(display); DisplayVk *displayVk = vk::GetImpl(display);
VkDevice device = displayVk->getDevice(); RendererVk *renderer = displayVk->getRenderer();
image.releaseImage(renderer);
// It should be safe to immediately destroy the backing images of a surface on surface image.releaseStagingBuffer(renderer);
// destruction. If this assumption is incorrect, we could use the last submit serial imageViews.release(renderer);
// to determine when to destroy the surface.
image.destroy(device);
imageViews.destroy(device);
} }
OffscreenSurfaceVk::OffscreenSurfaceVk(const egl::SurfaceState &surfaceState) OffscreenSurfaceVk::OffscreenSurfaceVk(const egl::SurfaceState &surfaceState)
......
...@@ -29,8 +29,9 @@ namespace rx ...@@ -29,8 +29,9 @@ namespace rx
{ {
namespace namespace
{ {
constexpr VkImageUsageFlags kDrawStagingImageFlags = constexpr VkImageUsageFlags kDrawStagingImageFlags = VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
constexpr VkImageUsageFlags kTransferStagingImageFlags = constexpr VkImageUsageFlags kTransferStagingImageFlags =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
...@@ -1654,6 +1655,7 @@ angle::Result TextureVk::initializeContents(const gl::Context *context, ...@@ -1654,6 +1655,7 @@ angle::Result TextureVk::initializeContents(const gl::Context *context,
vk::GetImpl(context)->getRenderer()->getFormat(desc.format.info->sizedInternalFormat); vk::GetImpl(context)->getRenderer()->getFormat(desc.format.info->sizedInternalFormat);
mImage->stageSubresourceRobustClear(imageIndex, format.intendedFormat()); mImage->stageSubresourceRobustClear(imageIndex, format.intendedFormat());
onStagingBufferChange();
// Note that we cannot ensure the image is initialized because we might be calling subImage // Note that we cannot ensure the image is initialized because we might be calling subImage
// on a non-complete cube map. // on a non-complete cube map.
......
...@@ -1473,7 +1473,7 @@ angle::Result BufferHelper::init(ContextVk *contextVk, ...@@ -1473,7 +1473,7 @@ angle::Result BufferHelper::init(ContextVk *contextVk,
const VkBufferCreateInfo &requestedCreateInfo, const VkBufferCreateInfo &requestedCreateInfo,
VkMemoryPropertyFlags memoryPropertyFlags) VkMemoryPropertyFlags memoryPropertyFlags)
{ {
RendererVk *rendererVk = contextVk->getRenderer(); RendererVk *renderer = contextVk->getRenderer();
// TODO: Remove with anglebug.com/2162: Vulkan: Implement device memory sub-allocation // TODO: Remove with anglebug.com/2162: Vulkan: Implement device memory sub-allocation
// Check if we have too many resources allocated already and need to free some before allocating // Check if we have too many resources allocated already and need to free some before allocating
...@@ -1488,9 +1488,9 @@ angle::Result BufferHelper::init(ContextVk *contextVk, ...@@ -1488,9 +1488,9 @@ angle::Result BufferHelper::init(ContextVk *contextVk,
VkBufferCreateInfo modifiedCreateInfo; VkBufferCreateInfo modifiedCreateInfo;
const VkBufferCreateInfo *createInfo = &requestedCreateInfo; const VkBufferCreateInfo *createInfo = &requestedCreateInfo;
if (rendererVk->getFeatures().roundUpBuffersToMaxVertexAttribStride.enabled) if (renderer->getFeatures().roundUpBuffersToMaxVertexAttribStride.enabled)
{ {
const VkDeviceSize maxVertexAttribStride = rendererVk->getMaxVertexAttribStride(); const VkDeviceSize maxVertexAttribStride = renderer->getMaxVertexAttribStride();
ASSERT(maxVertexAttribStride); ASSERT(maxVertexAttribStride);
modifiedCreateInfo = requestedCreateInfo; modifiedCreateInfo = requestedCreateInfo;
modifiedCreateInfo.size = roundUp(modifiedCreateInfo.size, maxVertexAttribStride); modifiedCreateInfo.size = roundUp(modifiedCreateInfo.size, maxVertexAttribStride);
...@@ -1498,9 +1498,55 @@ angle::Result BufferHelper::init(ContextVk *contextVk, ...@@ -1498,9 +1498,55 @@ angle::Result BufferHelper::init(ContextVk *contextVk,
} }
ANGLE_VK_TRY(contextVk, mBuffer.init(contextVk->getDevice(), *createInfo)); ANGLE_VK_TRY(contextVk, mBuffer.init(contextVk->getDevice(), *createInfo));
VkDeviceSize size;
ANGLE_TRY(AllocateBufferMemory(contextVk, memoryPropertyFlags, &mMemoryPropertyFlags, nullptr, ANGLE_TRY(AllocateBufferMemory(contextVk, memoryPropertyFlags, &mMemoryPropertyFlags, nullptr,
&mBuffer, &mDeviceMemory)); &mBuffer, &mDeviceMemory, &size));
mCurrentQueueFamilyIndex = contextVk->getRenderer()->getQueueFamilyIndex(); mCurrentQueueFamilyIndex = contextVk->getRenderer()->getQueueFamilyIndex();
if (renderer->getFeatures().allocateNonZeroMemory.enabled)
{
// This memory can't be mapped, so the buffer must be marked as a transfer destination so we
// can use a staging resource to initialize it to a non-zero value. If the memory is
// mappable we do the initialization in AllocateBufferMemory.
if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0 &&
(requestedCreateInfo.usage & VK_BUFFER_USAGE_TRANSFER_DST_BIT) != 0)
{
ANGLE_TRY(initializeNonZeroMemory(contextVk, size));
}
}
return angle::Result::Continue;
}
angle::Result BufferHelper::initializeNonZeroMemory(Context *context, VkDeviceSize size)
{
// Staging buffer memory is non-zero-initialized in 'init'.
StagingBuffer stagingBuffer;
ANGLE_TRY(stagingBuffer.init(context, size, StagingUsage::Both));
RendererVk *renderer = context->getRenderer();
vk::PrimaryCommandBuffer commandBuffer;
ANGLE_TRY(renderer->getCommandBufferOneOff(context, &commandBuffer));
// Queue a DMA copy.
VkBufferCopy copyRegion = {};
copyRegion.srcOffset = 0;
copyRegion.dstOffset = 0;
copyRegion.size = size;
commandBuffer.copyBuffer(stagingBuffer.getBuffer(), mBuffer, 1, &copyRegion);
ANGLE_VK_TRY(context, commandBuffer.end());
Serial serial;
ANGLE_TRY(renderer->queueSubmitOneOff(context, std::move(commandBuffer),
egl::ContextPriority::Medium, &serial));
stagingBuffer.collectGarbage(renderer, serial);
mUse.updateSerialOneOff(serial);
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -1875,13 +1921,69 @@ void ImageHelper::resetImageWeakReference() ...@@ -1875,13 +1921,69 @@ void ImageHelper::resetImageWeakReference()
mImage.reset(); mImage.reset();
} }
angle::Result ImageHelper::initializeNonZeroMemory(Context *context, VkDeviceSize size)
{
// The staging buffer memory is non-zero-initialized in 'init'.
vk::StagingBuffer stagingBuffer;
ANGLE_TRY(stagingBuffer.init(context, size, vk::StagingUsage::Write));
RendererVk *renderer = context->getRenderer();
vk::PrimaryCommandBuffer commandBuffer;
ANGLE_TRY(renderer->getCommandBufferOneOff(context, &commandBuffer));
// Queue a DMA copy.
forceChangeLayoutAndQueue(getAspectFlags(), ImageLayout::TransferDst, mCurrentQueueFamilyIndex,
&commandBuffer);
VkBufferImageCopy copyRegion = {};
copyRegion.imageExtent = mExtents;
copyRegion.imageSubresource.aspectMask = getAspectFlags();
copyRegion.imageSubresource.layerCount = 1;
commandBuffer.copyBufferToImage(stagingBuffer.getBuffer().getHandle(), mImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copyRegion);
ANGLE_VK_TRY(context, commandBuffer.end());
Serial serial;
ANGLE_TRY(renderer->queueSubmitOneOff(context, std::move(commandBuffer),
egl::ContextPriority::Medium, &serial));
stagingBuffer.collectGarbage(renderer, serial);
mUse.updateSerialOneOff(serial);
return angle::Result::Continue;
}
angle::Result ImageHelper::initMemory(Context *context, angle::Result ImageHelper::initMemory(Context *context,
const MemoryProperties &memoryProperties, const MemoryProperties &memoryProperties,
VkMemoryPropertyFlags flags) VkMemoryPropertyFlags flags)
{ {
// TODO(jmadill): Memory sub-allocation. http://anglebug.com/2162 // TODO(jmadill): Memory sub-allocation. http://anglebug.com/2162
ANGLE_TRY(AllocateImageMemory(context, flags, nullptr, &mImage, &mDeviceMemory)); VkDeviceSize size;
ANGLE_TRY(AllocateImageMemory(context, flags, nullptr, &mImage, &mDeviceMemory, &size));
mCurrentQueueFamilyIndex = context->getRenderer()->getQueueFamilyIndex(); mCurrentQueueFamilyIndex = context->getRenderer()->getQueueFamilyIndex();
RendererVk *renderer = context->getRenderer();
if (renderer->getFeatures().allocateNonZeroMemory.enabled)
{
// Can't map the memory. Use a staging resource.
if ((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
{
// Only currently works with single-sampled color images with one mip/layer.
if (mLevelCount == 1 && mLayerCount == 1 &&
getAspectFlags() == VK_IMAGE_ASPECT_COLOR_BIT && mSamples == 1)
{
ANGLE_TRY(initializeNonZeroMemory(context, size));
}
else
{
UNIMPLEMENTED();
}
}
}
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -3578,29 +3680,47 @@ void ImageViewHelper::release(RendererVk *renderer) ...@@ -3578,29 +3680,47 @@ void ImageViewHelper::release(RendererVk *renderer)
{ {
std::vector<GarbageObject> garbage; std::vector<GarbageObject> garbage;
if (mReadImageView.valid())
{
garbage.emplace_back(GetGarbage(&mReadImageView)); garbage.emplace_back(GetGarbage(&mReadImageView));
}
if (mFetchImageView.valid())
{
garbage.emplace_back(GetGarbage(&mFetchImageView)); garbage.emplace_back(GetGarbage(&mFetchImageView));
}
if (mStencilReadImageView.valid())
{
garbage.emplace_back(GetGarbage(&mStencilReadImageView)); garbage.emplace_back(GetGarbage(&mStencilReadImageView));
}
for (ImageView &imageView : mLevelDrawImageViews) for (ImageView &imageView : mLevelDrawImageViews)
{ {
if (imageView.valid())
{
garbage.emplace_back(GetGarbage(&imageView)); garbage.emplace_back(GetGarbage(&imageView));
} }
}
mLevelDrawImageViews.clear(); mLevelDrawImageViews.clear();
for (ImageViewVector &layerViews : mLayerLevelDrawImageViews) for (ImageViewVector &layerViews : mLayerLevelDrawImageViews)
{ {
for (ImageView &imageView : layerViews) for (ImageView &imageView : layerViews)
{ {
if (imageView.valid())
{
garbage.emplace_back(GetGarbage(&imageView)); garbage.emplace_back(GetGarbage(&imageView));
} }
} }
}
mLayerLevelDrawImageViews.clear(); mLayerLevelDrawImageViews.clear();
if (!garbage.empty())
{
renderer->collectGarbage(std::move(mUse), std::move(garbage)); renderer->collectGarbage(std::move(mUse), std::move(garbage));
// Ensure the resource use is always valid. // Ensure the resource use is always valid.
mUse.init(); mUse.init();
}
} }
void ImageViewHelper::destroy(VkDevice device) void ImageViewHelper::destroy(VkDevice device)
......
...@@ -598,6 +598,8 @@ class BufferHelper final : public CommandGraphResource ...@@ -598,6 +598,8 @@ class BufferHelper final : public CommandGraphResource
VkAccessFlags *barrierDstOut); VkAccessFlags *barrierDstOut);
void onWriteAccess(ContextVk *contextVk, VkAccessFlags writeAccessType); void onWriteAccess(ContextVk *contextVk, VkAccessFlags writeAccessType);
angle::Result initializeNonZeroMemory(Context *context, VkDeviceSize size);
// Vulkan objects. // Vulkan objects.
Buffer mBuffer; Buffer mBuffer;
BufferView mBufferView; BufferView mBufferView;
...@@ -1003,6 +1005,8 @@ class ImageHelper final : public CommandGraphResource ...@@ -1003,6 +1005,8 @@ class ImageHelper final : public CommandGraphResource
uint32_t layerCount, uint32_t layerCount,
CommandBuffer *commandBuffer); CommandBuffer *commandBuffer);
angle::Result initializeNonZeroMemory(Context *context, VkDeviceSize size);
enum class UpdateSource enum class UpdateSource
{ {
Clear, Clear,
......
...@@ -280,8 +280,8 @@ using GarbageList = std::vector<GarbageObject>; ...@@ -280,8 +280,8 @@ using GarbageList = std::vector<GarbageObject>;
// A list of garbage objects and the associated serial after which the objects can be destroyed. // A list of garbage objects and the associated serial after which the objects can be destroyed.
using GarbageAndSerial = ObjectAndSerial<GarbageList>; using GarbageAndSerial = ObjectAndSerial<GarbageList>;
// Houses multiple lists of garbage objects. Each sub-list has a different lifetime. They should // Houses multiple lists of garbage objects. Each sub-list has a different lifetime. They should be
// be sorted such that later-living garbage is ordered later in the list. // sorted such that later-living garbage is ordered later in the list.
using GarbageQueue = std::vector<GarbageAndSerial>; using GarbageQueue = std::vector<GarbageAndSerial>;
class MemoryProperties final : angle::NonCopyable class MemoryProperties final : angle::NonCopyable
...@@ -307,9 +307,10 @@ class StagingBuffer final : angle::NonCopyable ...@@ -307,9 +307,10 @@ class StagingBuffer final : angle::NonCopyable
public: public:
StagingBuffer(); StagingBuffer();
void release(ContextVk *contextVk); void release(ContextVk *contextVk);
void collectGarbage(RendererVk *renderer, Serial serial);
void destroy(VkDevice device); void destroy(VkDevice device);
angle::Result init(ContextVk *context, VkDeviceSize size, StagingUsage usage); angle::Result init(Context *context, VkDeviceSize size, StagingUsage usage);
Buffer &getBuffer() { return mBuffer; } Buffer &getBuffer() { return mBuffer; }
const Buffer &getBuffer() const { return mBuffer; } const Buffer &getBuffer() const { return mBuffer; }
...@@ -323,18 +324,26 @@ class StagingBuffer final : angle::NonCopyable ...@@ -323,18 +324,26 @@ class StagingBuffer final : angle::NonCopyable
size_t mSize; size_t mSize;
}; };
angle::Result InitMappableDeviceMemory(vk::Context *context,
vk::DeviceMemory *deviceMemory,
VkDeviceSize size,
int value);
angle::Result AllocateBufferMemory(Context *context, angle::Result AllocateBufferMemory(Context *context,
VkMemoryPropertyFlags requestedMemoryPropertyFlags, VkMemoryPropertyFlags requestedMemoryPropertyFlags,
VkMemoryPropertyFlags *memoryPropertyFlagsOut, VkMemoryPropertyFlags *memoryPropertyFlagsOut,
const void *extraAllocationInfo, const void *extraAllocationInfo,
Buffer *buffer, Buffer *buffer,
DeviceMemory *deviceMemoryOut); DeviceMemory *deviceMemoryOut,
VkDeviceSize *sizeOut);
angle::Result AllocateImageMemory(Context *context, angle::Result AllocateImageMemory(Context *context,
VkMemoryPropertyFlags memoryPropertyFlags, VkMemoryPropertyFlags memoryPropertyFlags,
const void *extraAllocationInfo, const void *extraAllocationInfo,
Image *image, Image *image,
DeviceMemory *deviceMemoryOut); DeviceMemory *deviceMemoryOut,
VkDeviceSize *sizeOut);
angle::Result AllocateImageMemoryWithRequirements(Context *context, angle::Result AllocateImageMemoryWithRequirements(Context *context,
VkMemoryPropertyFlags memoryPropertyFlags, VkMemoryPropertyFlags memoryPropertyFlags,
const VkMemoryRequirements &memoryRequirements, const VkMemoryRequirements &memoryRequirements,
......
...@@ -131,6 +131,9 @@ class CommandPool final : public WrappedObject<CommandPool, VkCommandPool> ...@@ -131,6 +131,9 @@ class CommandPool final : public WrappedObject<CommandPool, VkCommandPool>
void destroy(VkDevice device); void destroy(VkDevice device);
VkResult reset(VkDevice device, VkCommandPoolResetFlags flags); VkResult reset(VkDevice device, VkCommandPoolResetFlags flags);
void freeCommandBuffers(VkDevice device,
uint32_t commandBufferCount,
const VkCommandBuffer *commandBuffers);
VkResult init(VkDevice device, const VkCommandPoolCreateInfo &createInfo); VkResult init(VkDevice device, const VkCommandPoolCreateInfo &createInfo);
}; };
...@@ -609,6 +612,14 @@ ANGLE_INLINE VkResult CommandPool::reset(VkDevice device, VkCommandPoolResetFlag ...@@ -609,6 +612,14 @@ ANGLE_INLINE VkResult CommandPool::reset(VkDevice device, VkCommandPoolResetFlag
return vkResetCommandPool(device, mHandle, flags); return vkResetCommandPool(device, mHandle, flags);
} }
ANGLE_INLINE void CommandPool::freeCommandBuffers(VkDevice device,
uint32_t commandBufferCount,
const VkCommandBuffer *commandBuffers)
{
ASSERT(valid());
vkFreeCommandBuffers(device, mHandle, commandBufferCount, commandBuffers);
}
ANGLE_INLINE VkResult CommandPool::init(VkDevice device, const VkCommandPoolCreateInfo &createInfo) ANGLE_INLINE VkResult CommandPool::init(VkDevice device, const VkCommandPoolCreateInfo &createInfo)
{ {
ASSERT(!valid()); ASSERT(!valid());
......
...@@ -1975,10 +1975,12 @@ TEST_P(RobustResourceInitTestES3, InitializeMultisampledDepthRenderbufferAfterCo ...@@ -1975,10 +1975,12 @@ TEST_P(RobustResourceInitTestES3, InitializeMultisampledDepthRenderbufferAfterCo
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
} }
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(RobustResourceInitTest); ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND(RobustResourceInitTest,
WithAllocateNonZeroMemory(ES2_VULKAN()));
ANGLE_INSTANTIATE_TEST_ES3(RobustResourceInitTestES3); ANGLE_INSTANTIATE_TEST_ES3_AND(RobustResourceInitTestES3, WithAllocateNonZeroMemory(ES3_VULKAN()));
ANGLE_INSTANTIATE_TEST_ES31(RobustResourceInitTestES31); ANGLE_INSTANTIATE_TEST_ES31_AND(RobustResourceInitTestES31,
WithAllocateNonZeroMemory(ES31_VULKAN()));
} // namespace angle } // namespace angle
...@@ -418,7 +418,6 @@ ANGLE_INSTANTIATE_TEST(TextureUploadFullMipBenchmark, ...@@ -418,7 +418,6 @@ ANGLE_INSTANTIATE_TEST(TextureUploadFullMipBenchmark,
OpenGLOrGLESParams(false), OpenGLOrGLESParams(false),
OpenGLOrGLESParams(true), OpenGLOrGLESParams(true),
VulkanParams(false), VulkanParams(false),
NullDevice(VulkanParams(false)),
VulkanParams(true)); VulkanParams(true));
ANGLE_INSTANTIATE_TEST(PBOSubImageBenchmark, ANGLE_INSTANTIATE_TEST(PBOSubImageBenchmark,
......
...@@ -196,6 +196,15 @@ std::ostream &operator<<(std::ostream &stream, const PlatformParameters &pp) ...@@ -196,6 +196,15 @@ std::ostream &operator<<(std::ostream &stream, const PlatformParameters &pp)
stream << "_TransformFeedback"; stream << "_TransformFeedback";
} }
if (pp.eglParameters.allocateNonZeroMemoryFeature == EGL_FALSE)
{
stream << "_NoAllocateNonZeroMemory";
}
else if (pp.eglParameters.allocateNonZeroMemoryFeature == EGL_TRUE)
{
stream << "_AllocateNonZeroMemory";
}
return stream; return stream;
} }
......
...@@ -214,6 +214,12 @@ inline PlatformParameters WithNoTransformFeedback(const PlatformParameters &para ...@@ -214,6 +214,12 @@ inline PlatformParameters WithNoTransformFeedback(const PlatformParameters &para
return withNoTransformFeedback; return withNoTransformFeedback;
} }
inline PlatformParameters WithAllocateNonZeroMemory(const PlatformParameters &params)
{
PlatformParameters allocateNonZero = params;
allocateNonZero.eglParameters.allocateNonZeroMemoryFeature = EGL_TRUE;
return allocateNonZero;
}
} // namespace angle } // namespace angle
#endif // ANGLE_TEST_CONFIGS_H_ #endif // ANGLE_TEST_CONFIGS_H_
...@@ -150,12 +150,22 @@ struct CombinedPrintToStringParamName ...@@ -150,12 +150,22 @@ struct CombinedPrintToStringParamName
INSTANTIATE_TEST_SUITE_P(, testName, ANGLE_INSTANTIATE_TEST_PLATFORMS(testName), \ INSTANTIATE_TEST_SUITE_P(, testName, ANGLE_INSTANTIATE_TEST_PLATFORMS(testName), \
testing::PrintToStringParamName()) testing::PrintToStringParamName())
#define ANGLE_INSTANTIATE_TEST_ES3_AND(testName, extra) \
const PlatformParameters testName##params[] = {ANGLE_ALL_TEST_PLATFORMS_ES3, extra}; \
INSTANTIATE_TEST_SUITE_P(, testName, ANGLE_INSTANTIATE_TEST_PLATFORMS(testName), \
testing::PrintToStringParamName())
// Instantiate the test once for each GLES31 platform // Instantiate the test once for each GLES31 platform
#define ANGLE_INSTANTIATE_TEST_ES31(testName) \ #define ANGLE_INSTANTIATE_TEST_ES31(testName) \
const PlatformParameters testName##params[] = {ANGLE_ALL_TEST_PLATFORMS_ES31}; \ const PlatformParameters testName##params[] = {ANGLE_ALL_TEST_PLATFORMS_ES31}; \
INSTANTIATE_TEST_SUITE_P(, testName, ANGLE_INSTANTIATE_TEST_PLATFORMS(testName), \ INSTANTIATE_TEST_SUITE_P(, testName, ANGLE_INSTANTIATE_TEST_PLATFORMS(testName), \
testing::PrintToStringParamName()) testing::PrintToStringParamName())
#define ANGLE_INSTANTIATE_TEST_ES31_AND(testName, extra) \
const PlatformParameters testName##params[] = {ANGLE_ALL_TEST_PLATFORMS_ES31, extra}; \
INSTANTIATE_TEST_SUITE_P(, testName, ANGLE_INSTANTIATE_TEST_PLATFORMS(testName), \
testing::PrintToStringParamName())
// Multiple ES Version macros // Multiple ES Version macros
#define ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(testName) \ #define ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(testName) \
const PlatformParameters testName##params[] = {ANGLE_ALL_TEST_PLATFORMS_ES2, \ const PlatformParameters testName##params[] = {ANGLE_ALL_TEST_PLATFORMS_ES2, \
......
...@@ -49,7 +49,7 @@ struct EGLPlatformParameters ...@@ -49,7 +49,7 @@ struct EGLPlatformParameters
{ {
return std::tie(renderer, majorVersion, minorVersion, deviceType, presentPath, return std::tie(renderer, majorVersion, minorVersion, deviceType, presentPath,
debugLayersEnabled, contextVirtualization, transformFeedbackFeature, debugLayersEnabled, contextVirtualization, transformFeedbackFeature,
platformMethods); allocateNonZeroMemoryFeature, platformMethods);
} }
EGLint renderer = EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE; EGLint renderer = EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE;
...@@ -60,6 +60,7 @@ struct EGLPlatformParameters ...@@ -60,6 +60,7 @@ struct EGLPlatformParameters
EGLint debugLayersEnabled = EGL_DONT_CARE; EGLint debugLayersEnabled = EGL_DONT_CARE;
EGLint contextVirtualization = EGL_DONT_CARE; EGLint contextVirtualization = EGL_DONT_CARE;
EGLint transformFeedbackFeature = EGL_DONT_CARE; EGLint transformFeedbackFeature = EGL_DONT_CARE;
EGLint allocateNonZeroMemoryFeature = EGL_DONT_CARE;
angle::PlatformMethods *platformMethods = nullptr; angle::PlatformMethods *platformMethods = nullptr;
}; };
......
...@@ -179,6 +179,15 @@ bool EGLWindow::initializeDisplay(OSWindow *osWindow, ...@@ -179,6 +179,15 @@ bool EGLWindow::initializeDisplay(OSWindow *osWindow,
disabledFeatureOverrides.push_back("emulate_transform_feedback"); disabledFeatureOverrides.push_back("emulate_transform_feedback");
} }
if (params.allocateNonZeroMemoryFeature == EGL_TRUE)
{
enabledFeatureOverrides.push_back("allocate_non_zero_memory");
}
else if (params.allocateNonZeroMemoryFeature == EGL_FALSE)
{
disabledFeatureOverrides.push_back("allocate_non_zero_memory");
}
if (!disabledFeatureOverrides.empty()) if (!disabledFeatureOverrides.empty())
{ {
if (strstr(extensionString, "EGL_ANGLE_feature_control") == nullptr) if (strstr(extensionString, "EGL_ANGLE_feature_control") == nullptr)
......
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