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
Feature supportsExternalMemoryHost = {
"supports_external_memory_host", FeatureCategory::VulkanFeatures,
"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;
......
......@@ -11,6 +11,7 @@
#define LIBANGLE_RENDERER_SERIAL_UTILS_H_
#include <atomic>
#include <limits>
#include "common/angleutils.h"
#include "common/debug.h"
......@@ -45,6 +46,8 @@ class Serial final
constexpr Serial(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
{
return mValue != kInvalid && mValue == other.mValue;
......
......@@ -322,6 +322,9 @@ class SharedResourceUse final : angle::NonCopyable
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()
{
ASSERT(valid());
......
......@@ -468,7 +468,7 @@ angle::Result CommandQueue::submitFrame(vk::Context *context,
batch.fence.copy(device, sharedFence);
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())
{
......@@ -3941,7 +3941,7 @@ angle::Result ContextVk::getTimestamp(uint64_t *timestampOut)
Serial throwAwaySerial;
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
// in parallel with what's already running on the GPU.
......
......@@ -203,7 +203,8 @@ void DisplayVk::generateExtensions(egl::DisplayExtensions *outExtensions) const
outExtensions->framebufferTargetANDROID = true;
#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;
#if defined(ANGLE_PLATFORM_GGP)
......
......@@ -593,9 +593,27 @@ RendererVk::~RendererVk()
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);
ASSERT(mSharedGarbage.empty());
for (PendingOneOffCommands &pending : mPendingOneOffCommands)
{
pending.commandBuffer.releaseHandle();
}
mOneOffCommandPool.destroy(mDevice);
mFenceRecycler.destroy(mDevice);
mPipelineLayoutCache.destroy(mDevice);
......@@ -1597,6 +1615,10 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev
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(
(&mFeatures), supportsExternalMemoryHost,
ExtensionFound(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME, deviceExtensionNames));
......@@ -1828,12 +1850,13 @@ bool RendererVk::hasBufferFormatFeatureBits(VkFormat format, const VkFormatFeatu
angle::Result RendererVk::queueSubmit(vk::Context *context,
egl::ContextPriority priority,
const VkSubmitInfo &submitInfo,
const vk::Fence &fence,
const vk::Fence *fence,
Serial *serialOut)
{
{
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));
......@@ -1845,6 +1868,23 @@ angle::Result RendererVk::queueSubmit(vk::Context *context,
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)
{
{
......@@ -2000,4 +2040,42 @@ void RendererVk::reloadVolkIfNeeded() const
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
......@@ -10,8 +10,10 @@
#ifndef LIBANGLE_RENDERER_VULKAN_RENDERERVK_H_
#define LIBANGLE_RENDERER_VULKAN_RENDERERVK_H_
#include <deque>
#include <memory>
#include <mutex>
#include "vk_ext_provoking_vertex.h"
#include "volk.h"
......@@ -169,12 +171,23 @@ class RendererVk : angle::NonCopyable
angle::Result queueSubmit(vk::Context *context,
egl::ContextPriority priority,
const VkSubmitInfo &submitInfo,
const vk::Fence &fence,
const vk::Fence *fence,
Serial *serialOut);
angle::Result queueWaitIdle(vk::Context *context, egl::ContextPriority priority);
angle::Result deviceWaitIdle(vk::Context *context);
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);
inline void resetSharedFence(vk::Shared<vk::Fence> *sharedFenceIn)
{
......@@ -201,7 +214,10 @@ class RendererVk : angle::NonCopyable
void collectGarbage(vk::SharedResourceUse &&use, std::vector<vk::GarbageObject> &&sharedGarbage)
{
mSharedGarbage.emplace_back(std::move(use), std::move(sharedGarbage));
if (!sharedGarbage.empty())
{
mSharedGarbage.emplace_back(std::move(use), std::move(sharedGarbage));
}
}
static constexpr size_t kMaxExtensionNames = 200;
......@@ -326,6 +342,16 @@ class RendererVk : angle::NonCopyable
static constexpr double kPercentMaxMemoryAllocationCount = 0.3;
// How many objects to garbage collect before issuing a flush()
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
......
......@@ -201,13 +201,10 @@ angle::Result OffscreenSurfaceVk::AttachmentImage::initializeWithExternalMemory(
void OffscreenSurfaceVk::AttachmentImage::destroy(const egl::Display *display)
{
DisplayVk *displayVk = vk::GetImpl(display);
VkDevice device = displayVk->getDevice();
// It should be safe to immediately destroy the backing images of a surface on surface
// destruction. If this assumption is incorrect, we could use the last submit serial
// to determine when to destroy the surface.
image.destroy(device);
imageViews.destroy(device);
RendererVk *renderer = displayVk->getRenderer();
image.releaseImage(renderer);
image.releaseStagingBuffer(renderer);
imageViews.release(renderer);
}
OffscreenSurfaceVk::OffscreenSurfaceVk(const egl::SurfaceState &surfaceState)
......
......@@ -29,8 +29,9 @@ namespace rx
{
namespace
{
constexpr VkImageUsageFlags kDrawStagingImageFlags =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
constexpr VkImageUsageFlags kDrawStagingImageFlags = VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
constexpr VkImageUsageFlags kTransferStagingImageFlags =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
......@@ -1654,6 +1655,7 @@ angle::Result TextureVk::initializeContents(const gl::Context *context,
vk::GetImpl(context)->getRenderer()->getFormat(desc.format.info->sizedInternalFormat);
mImage->stageSubresourceRobustClear(imageIndex, format.intendedFormat());
onStagingBufferChange();
// Note that we cannot ensure the image is initialized because we might be calling subImage
// on a non-complete cube map.
......
......@@ -1473,7 +1473,7 @@ angle::Result BufferHelper::init(ContextVk *contextVk,
const VkBufferCreateInfo &requestedCreateInfo,
VkMemoryPropertyFlags memoryPropertyFlags)
{
RendererVk *rendererVk = contextVk->getRenderer();
RendererVk *renderer = contextVk->getRenderer();
// 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
......@@ -1488,9 +1488,9 @@ angle::Result BufferHelper::init(ContextVk *contextVk,
VkBufferCreateInfo modifiedCreateInfo;
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);
modifiedCreateInfo = requestedCreateInfo;
modifiedCreateInfo.size = roundUp(modifiedCreateInfo.size, maxVertexAttribStride);
......@@ -1498,9 +1498,55 @@ angle::Result BufferHelper::init(ContextVk *contextVk,
}
ANGLE_VK_TRY(contextVk, mBuffer.init(contextVk->getDevice(), *createInfo));
VkDeviceSize size;
ANGLE_TRY(AllocateBufferMemory(contextVk, memoryPropertyFlags, &mMemoryPropertyFlags, nullptr,
&mBuffer, &mDeviceMemory));
&mBuffer, &mDeviceMemory, &size));
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;
}
......@@ -1875,13 +1921,69 @@ void ImageHelper::resetImageWeakReference()
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,
const MemoryProperties &memoryProperties,
VkMemoryPropertyFlags flags)
{
// 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();
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;
}
......@@ -3578,13 +3680,25 @@ void ImageViewHelper::release(RendererVk *renderer)
{
std::vector<GarbageObject> garbage;
garbage.emplace_back(GetGarbage(&mReadImageView));
garbage.emplace_back(GetGarbage(&mFetchImageView));
garbage.emplace_back(GetGarbage(&mStencilReadImageView));
if (mReadImageView.valid())
{
garbage.emplace_back(GetGarbage(&mReadImageView));
}
if (mFetchImageView.valid())
{
garbage.emplace_back(GetGarbage(&mFetchImageView));
}
if (mStencilReadImageView.valid())
{
garbage.emplace_back(GetGarbage(&mStencilReadImageView));
}
for (ImageView &imageView : mLevelDrawImageViews)
{
garbage.emplace_back(GetGarbage(&imageView));
if (imageView.valid())
{
garbage.emplace_back(GetGarbage(&imageView));
}
}
mLevelDrawImageViews.clear();
......@@ -3592,15 +3706,21 @@ void ImageViewHelper::release(RendererVk *renderer)
{
for (ImageView &imageView : layerViews)
{
garbage.emplace_back(GetGarbage(&imageView));
if (imageView.valid())
{
garbage.emplace_back(GetGarbage(&imageView));
}
}
}
mLayerLevelDrawImageViews.clear();
renderer->collectGarbage(std::move(mUse), std::move(garbage));
if (!garbage.empty())
{
renderer->collectGarbage(std::move(mUse), std::move(garbage));
// Ensure the resource use is always valid.
mUse.init();
// Ensure the resource use is always valid.
mUse.init();
}
}
void ImageViewHelper::destroy(VkDevice device)
......
......@@ -598,6 +598,8 @@ class BufferHelper final : public CommandGraphResource
VkAccessFlags *barrierDstOut);
void onWriteAccess(ContextVk *contextVk, VkAccessFlags writeAccessType);
angle::Result initializeNonZeroMemory(Context *context, VkDeviceSize size);
// Vulkan objects.
Buffer mBuffer;
BufferView mBufferView;
......@@ -1003,6 +1005,8 @@ class ImageHelper final : public CommandGraphResource
uint32_t layerCount,
CommandBuffer *commandBuffer);
angle::Result initializeNonZeroMemory(Context *context, VkDeviceSize size);
enum class UpdateSource
{
Clear,
......
......@@ -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.
using GarbageAndSerial = ObjectAndSerial<GarbageList>;
// Houses multiple lists of garbage objects. Each sub-list has a different lifetime. They should
// be sorted such that later-living garbage is ordered later in the list.
// Houses multiple lists of garbage objects. Each sub-list has a different lifetime. They should be
// sorted such that later-living garbage is ordered later in the list.
using GarbageQueue = std::vector<GarbageAndSerial>;
class MemoryProperties final : angle::NonCopyable
......@@ -307,9 +307,10 @@ class StagingBuffer final : angle::NonCopyable
public:
StagingBuffer();
void release(ContextVk *contextVk);
void collectGarbage(RendererVk *renderer, Serial serial);
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; }
const Buffer &getBuffer() const { return mBuffer; }
......@@ -323,18 +324,26 @@ class StagingBuffer final : angle::NonCopyable
size_t mSize;
};
angle::Result InitMappableDeviceMemory(vk::Context *context,
vk::DeviceMemory *deviceMemory,
VkDeviceSize size,
int value);
angle::Result AllocateBufferMemory(Context *context,
VkMemoryPropertyFlags requestedMemoryPropertyFlags,
VkMemoryPropertyFlags *memoryPropertyFlagsOut,
const void *extraAllocationInfo,
Buffer *buffer,
DeviceMemory *deviceMemoryOut);
DeviceMemory *deviceMemoryOut,
VkDeviceSize *sizeOut);
angle::Result AllocateImageMemory(Context *context,
VkMemoryPropertyFlags memoryPropertyFlags,
const void *extraAllocationInfo,
Image *image,
DeviceMemory *deviceMemoryOut);
DeviceMemory *deviceMemoryOut,
VkDeviceSize *sizeOut);
angle::Result AllocateImageMemoryWithRequirements(Context *context,
VkMemoryPropertyFlags memoryPropertyFlags,
const VkMemoryRequirements &memoryRequirements,
......
......@@ -131,6 +131,9 @@ class CommandPool final : public WrappedObject<CommandPool, VkCommandPool>
void destroy(VkDevice device);
VkResult reset(VkDevice device, VkCommandPoolResetFlags flags);
void freeCommandBuffers(VkDevice device,
uint32_t commandBufferCount,
const VkCommandBuffer *commandBuffers);
VkResult init(VkDevice device, const VkCommandPoolCreateInfo &createInfo);
};
......@@ -609,6 +612,14 @@ ANGLE_INLINE VkResult CommandPool::reset(VkDevice device, VkCommandPoolResetFlag
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)
{
ASSERT(!valid());
......
......@@ -1975,10 +1975,12 @@ TEST_P(RobustResourceInitTestES3, InitializeMultisampledDepthRenderbufferAfterCo
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
......@@ -418,7 +418,6 @@ ANGLE_INSTANTIATE_TEST(TextureUploadFullMipBenchmark,
OpenGLOrGLESParams(false),
OpenGLOrGLESParams(true),
VulkanParams(false),
NullDevice(VulkanParams(false)),
VulkanParams(true));
ANGLE_INSTANTIATE_TEST(PBOSubImageBenchmark,
......
......@@ -196,6 +196,15 @@ std::ostream &operator<<(std::ostream &stream, const PlatformParameters &pp)
stream << "_TransformFeedback";
}
if (pp.eglParameters.allocateNonZeroMemoryFeature == EGL_FALSE)
{
stream << "_NoAllocateNonZeroMemory";
}
else if (pp.eglParameters.allocateNonZeroMemoryFeature == EGL_TRUE)
{
stream << "_AllocateNonZeroMemory";
}
return stream;
}
......
......@@ -214,6 +214,12 @@ inline PlatformParameters WithNoTransformFeedback(const PlatformParameters &para
return withNoTransformFeedback;
}
inline PlatformParameters WithAllocateNonZeroMemory(const PlatformParameters &params)
{
PlatformParameters allocateNonZero = params;
allocateNonZero.eglParameters.allocateNonZeroMemoryFeature = EGL_TRUE;
return allocateNonZero;
}
} // namespace angle
#endif // ANGLE_TEST_CONFIGS_H_
......@@ -150,12 +150,22 @@ struct CombinedPrintToStringParamName
INSTANTIATE_TEST_SUITE_P(, testName, ANGLE_INSTANTIATE_TEST_PLATFORMS(testName), \
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
#define ANGLE_INSTANTIATE_TEST_ES31(testName) \
const PlatformParameters testName##params[] = {ANGLE_ALL_TEST_PLATFORMS_ES31}; \
INSTANTIATE_TEST_SUITE_P(, testName, ANGLE_INSTANTIATE_TEST_PLATFORMS(testName), \
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
#define ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(testName) \
const PlatformParameters testName##params[] = {ANGLE_ALL_TEST_PLATFORMS_ES2, \
......
......@@ -49,7 +49,7 @@ struct EGLPlatformParameters
{
return std::tie(renderer, majorVersion, minorVersion, deviceType, presentPath,
debugLayersEnabled, contextVirtualization, transformFeedbackFeature,
platformMethods);
allocateNonZeroMemoryFeature, platformMethods);
}
EGLint renderer = EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE;
......@@ -60,6 +60,7 @@ struct EGLPlatformParameters
EGLint debugLayersEnabled = EGL_DONT_CARE;
EGLint contextVirtualization = EGL_DONT_CARE;
EGLint transformFeedbackFeature = EGL_DONT_CARE;
EGLint allocateNonZeroMemoryFeature = EGL_DONT_CARE;
angle::PlatformMethods *platformMethods = nullptr;
};
......
......@@ -179,6 +179,15 @@ bool EGLWindow::initializeDisplay(OSWindow *osWindow,
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 (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