#include <string>
#include <vector>
-// \todo Check bufferImageGranularity
+// \todo [2016-03-09 mika] Check bufferImageGranularity
using tcu::TestLog;
using tcu::Maybe;
USAGE_UNIFORM_TEXEL_BUFFER = (0x1u<<8),
USAGE_STORAGE_TEXEL_BUFFER = (0x1u<<9),
- // \todo This is probably almost impossible to do
+ // \todo [2016-03-09 mika] This is probably almost impossible to do
USAGE_INDIRECT_BUFFER = (0x1u<<10),
// Texture usage flags
{
const deUint8* data = (const deUint8*)data_;
- // \todo Optimize
+ // \todo [2016-03-09 mika] Optimize
for (size_t pos = 0; pos < size; pos++)
{
m_data[offset + pos] = data[pos];
void ReferenceMemory::setUndefined (size_t offset, size_t size)
{
- // \todo Optimize
+ // \todo [2016-03-09 mika] Optimize
for (size_t pos = 0; pos < size; pos++)
m_defined[(offset + pos) / 64] |= 0x1ull << ((offset + pos) % 64);
}
vk::VkDeviceSize bestW = de::max(maxTexelCount, maxTextureSize);
vk::VkDeviceSize bestH = maxTexelCount / bestW;
- // \todo Could probably be faster?
+ // \todo [2016-03-09 mika] Could probably be faster?
for (vk::VkDeviceSize w = 1; w * w < maxTexelCount && w < maxTextureSize && bestW * bestH * 4 < size; w++)
{
const vk::VkDeviceSize h = maxTexelCount / w;
DE_ASSERT((size % 4) == 0);
- // \todo Could probably be faster?
+ // \todo [2016-03-09 mika] Could probably be faster?
for (vk::VkDeviceSize w = 1; w < maxTextureSize && w < texelCount; w++)
{
const vk::VkDeviceSize h = texelCount / w;
class ImageTransition : public CmdCommand
{
public:
- ImageTransition (void) {}
+ ImageTransition (vk::VkPipelineStageFlags srcStages,
+ vk::VkAccessFlags srcAccesses,
+
+ vk::VkPipelineStageFlags dstStages,
+ vk::VkAccessFlags dstAccesses,
+
+ vk::VkImageLayout srcLayout,
+ vk::VkImageLayout dstLayout);
+
~ImageTransition (void) {}
const char* getName (void) const { return "ImageTransition"; }
void verify (VerifyContext& context, size_t);
private:
- vk::VkDeviceSize m_imageMemorySize;
+ const vk::VkPipelineStageFlags m_srcStages;
+ const vk::VkAccessFlags m_srcAccesses;
+ const vk::VkPipelineStageFlags m_dstStages;
+ const vk::VkAccessFlags m_dstAccesses;
+ const vk::VkImageLayout m_srcLayout;
+ const vk::VkImageLayout m_dstLayout;
+
+ vk::VkDeviceSize m_imageMemorySize;
};
+ImageTransition::ImageTransition (vk::VkPipelineStageFlags srcStages,
+ vk::VkAccessFlags srcAccesses,
+
+ vk::VkPipelineStageFlags dstStages,
+ vk::VkAccessFlags dstAccesses,
+
+ vk::VkImageLayout srcLayout,
+ vk::VkImageLayout dstLayout)
+ : m_srcStages (srcStages)
+ , m_srcAccesses (srcAccesses)
+ , m_dstStages (dstStages)
+ , m_dstAccesses (dstAccesses)
+ , m_srcLayout (srcLayout)
+ , m_dstLayout (dstLayout)
+{
+}
+
void ImageTransition::logSubmit (TestLog& log, size_t commandIndex) const
{
- log << TestLog::Message << commandIndex << ":" << getName() << " Use pipeline barrier to transition to VK_IMAGE_LAYOUT_GENERAL." << TestLog::EndMessage;
+ log << TestLog::Message << commandIndex << ":" << getName()
+ << " Image transition pipeline barrier"
+ << ", srcStages: " << vk::getPipelineStageFlagsStr(m_srcStages) << ", srcAccesses: " << vk::getAccessFlagsStr(m_srcAccesses)
+ << ", dstStages: " << vk::getPipelineStageFlagsStr(m_dstStages) << ", dstAccesses: " << vk::getAccessFlagsStr(m_dstAccesses)
+ << ", srcLayout: " << m_srcLayout << ", dstLayout: " << m_dstLayout << TestLog::EndMessage;
}
void ImageTransition::prepare (PrepareContext& context)
{
- context.setImageLayout(vk::VK_IMAGE_LAYOUT_GENERAL);
+ DE_ASSERT(context.getImageLayout() == vk::VK_IMAGE_LAYOUT_UNDEFINED || m_srcLayout == vk::VK_IMAGE_LAYOUT_UNDEFINED || context.getImageLayout() == m_srcLayout);
+
+ context.setImageLayout(m_dstLayout);
m_imageMemorySize = context.getImageMemorySize();
}
vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
DE_NULL,
- ALL_ACCESSES,
- ALL_ACCESSES,
+ m_srcAccesses,
+ m_dstAccesses,
- context.getImageLayout(),
- vk::VK_IMAGE_LAYOUT_GENERAL,
+ m_srcLayout,
+ m_dstLayout,
vk::VK_QUEUE_FAMILY_IGNORED,
vk::VK_QUEUE_FAMILY_IGNORED,
}
};
- vkd.cmdPipelineBarrier(cmd, ALL_PIPELINE_STAGES, ALL_PIPELINE_STAGES, (vk::VkDependencyFlags)0, 0, (const vk::VkMemoryBarrier*)DE_NULL, 0, (const vk::VkBufferMemoryBarrier*)DE_NULL, 1, &barrier);
+ vkd.cmdPipelineBarrier(cmd, m_srcStages, m_dstStages, (vk::VkDependencyFlags)0, 0, (const vk::VkMemoryBarrier*)DE_NULL, 0, (const vk::VkBufferMemoryBarrier*)DE_NULL, 1, &barrier);
}
void ImageTransition::verify (VerifyContext& context, size_t)
OP_IMAGE_DESTROY,
OP_IMAGE_BINDMEMORY,
- OP_IMAGE_TRANSITION_TO_GENERAL,
+ OP_IMAGE_TRANSITION_LAYOUT,
OP_IMAGE_COPY_TO_BUFFER,
OP_IMAGE_COPY_FROM_BUFFER,
STAGE_RENDER_PASS
};
-bool isWriteAccess (vk::VkAccessFlagBits access)
+vk::VkAccessFlags getWriteAccessFlags (void)
{
- switch (access)
- {
-
- case vk::VK_ACCESS_INDIRECT_COMMAND_READ_BIT: return false;
- case vk::VK_ACCESS_INDEX_READ_BIT: return false;
- case vk::VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT: return false;
- case vk::VK_ACCESS_UNIFORM_READ_BIT: return false;
- case vk::VK_ACCESS_INPUT_ATTACHMENT_READ_BIT: return false;
- case vk::VK_ACCESS_SHADER_READ_BIT: return false;
- case vk::VK_ACCESS_SHADER_WRITE_BIT: return true;
- case vk::VK_ACCESS_COLOR_ATTACHMENT_READ_BIT: return false;
- case vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT: return true;
- case vk::VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT: return false;
- case vk::VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT: return true;
- case vk::VK_ACCESS_TRANSFER_READ_BIT: return false;
- case vk::VK_ACCESS_TRANSFER_WRITE_BIT: return true;
- case vk::VK_ACCESS_HOST_READ_BIT: return false;
- case vk::VK_ACCESS_HOST_WRITE_BIT: return true;
- case vk::VK_ACCESS_MEMORY_READ_BIT: return false;
- case vk::VK_ACCESS_MEMORY_WRITE_BIT: return true;
+ return vk::VK_ACCESS_SHADER_WRITE_BIT
+ | vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
+ | vk::VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT
+ | vk::VK_ACCESS_TRANSFER_WRITE_BIT
+ | vk::VK_ACCESS_HOST_WRITE_BIT
+ | vk::VK_ACCESS_MEMORY_WRITE_BIT;
+}
- default:
- DE_FATAL("Unknown access");
- return true;
- }
+bool isWriteAccess (vk::VkAccessFlagBits access)
+{
+ return getWriteAccessFlags() & access;
}
class CacheState
{
public:
- CacheState (vk::VkPipelineStageFlags allowedStages, vk::VkAccessFlags allowedAccesses);
+ CacheState (vk::VkPipelineStageFlags allowedStages, vk::VkAccessFlags allowedAccesses);
- bool isValid (vk::VkPipelineStageFlagBits stage,
- vk::VkAccessFlagBits access) const;
+ bool isValid (vk::VkPipelineStageFlagBits stage,
+ vk::VkAccessFlagBits access) const;
- void perform (vk::VkPipelineStageFlagBits stage,
- vk::VkAccessFlagBits access);
+ void perform (vk::VkPipelineStageFlagBits stage,
+ vk::VkAccessFlagBits access);
- void submitCommandBuffer (void);
+ void submitCommandBuffer (void);
+ void waitForIdle (void);
- void getFullBarrier (vk::VkPipelineStageFlags& srcStages,
- vk::VkAccessFlags& srcAccesses,
- vk::VkPipelineStageFlags& dstStages,
- vk::VkAccessFlags& dstAccesses) const;
+ void getFullBarrier (vk::VkPipelineStageFlags& srcStages,
+ vk::VkAccessFlags& srcAccesses,
+ vk::VkPipelineStageFlags& dstStages,
+ vk::VkAccessFlags& dstAccesses) const;
- void barrier (vk::VkPipelineStageFlags srcStages,
- vk::VkAccessFlags srcAccesses,
- vk::VkPipelineStageFlags dstStages,
- vk::VkAccessFlags dstAccesses);
+ void barrier (vk::VkPipelineStageFlags srcStages,
+ vk::VkAccessFlags srcAccesses,
+ vk::VkPipelineStageFlags dstStages,
+ vk::VkAccessFlags dstAccesses);
- void fullBarrier (void);
+ void imageLayoutBarrier (vk::VkPipelineStageFlags srcStages,
+ vk::VkAccessFlags srcAccesses,
+ vk::VkPipelineStageFlags dstStages,
+ vk::VkAccessFlags dstAccesses);
+
+ void checkImageLayoutBarrier (vk::VkPipelineStageFlags srcStages,
+ vk::VkAccessFlags srcAccesses,
+ vk::VkPipelineStageFlags dstStages,
+ vk::VkAccessFlags dstAccesses);
// Everything is clean and there is no need for barriers
- bool isClean (void) const;
+ bool isClean (void) const;
+ vk::VkPipelineStageFlags getAllowedStages (void) const { return m_allowedStages; }
+ vk::VkAccessFlags getAllowedAcceses (void) const { return m_allowedAccesses; }
private:
// Limit which stages and accesses are used by the CacheState tracker
const vk::VkPipelineStageFlags m_allowedStages;
// [dstStage][srcStage] = srcAccesses
// In stage dstStage write srcAccesses from srcStage are not yet available
vk::VkAccessFlags m_unavailableWriteOperations[PIPELINESTAGE_LAST][PIPELINESTAGE_LAST];
+ // Latest pipeline transition is not available in stage
+ bool m_unavailableLayoutTransition[PIPELINESTAGE_LAST];
// [dstStage] = dstAccesses
// In stage dstStage ops with dstAccesses are not yet visible
vk::VkAccessFlags m_invisibleOperations[PIPELINESTAGE_LAST];
// There are no incomplete read operations initially
m_incompleteOperations[dstStage] = 0;
+ // There are no incomplete layout transitions
+ m_unavailableLayoutTransition[dstStage] = false;
+
for (vk::VkPipelineStageFlags srcStage_ = 1; srcStage_ <= m_allowedStages; srcStage_ <<= 1)
{
const PipelineStage srcStage = pipelineStageFlagToPipelineStage((vk::VkPipelineStageFlagBits)srcStage_);
const PipelineStage dstStage = pipelineStageFlagToPipelineStage(stage);
// Previous operations are not visible to access on stage
- if ((m_invisibleOperations[dstStage] & access) != 0)
+ if (m_unavailableLayoutTransition[dstStage] || (m_invisibleOperations[dstStage] & access) != 0)
return false;
if (isWriteAccess(access))
m_allowedAccesses);
}
+void CacheState::waitForIdle (void)
+{
+ // Make all writes available
+ barrier(m_allowedStages,
+ m_allowedAccesses & getWriteAccessFlags(),
+ m_allowedStages,
+ 0);
+
+ // Make all writes visible on device side
+ barrier(m_allowedStages,
+ 0,
+ m_allowedStages & (~vk::VK_PIPELINE_STAGE_HOST_BIT),
+ m_allowedAccesses);
+}
+
void CacheState::getFullBarrier (vk::VkPipelineStageFlags& srcStages,
vk::VkAccessFlags& srcAccesses,
vk::VkPipelineStageFlags& dstStages,
srcStages |= dstStage_;
srcAccesses |= m_unavailableWriteOperations[dstStage][srcStage];
}
+
+ if (m_unavailableLayoutTransition[dstStage] && !m_unavailableLayoutTransition[srcStage])
+ {
+ // Add dependency between srcStage and dstStage if layout transition has not completed in dstStage,
+ // but has completed in srcStage.
+ dstStages |= dstStage_;
+ srcStages |= dstStage_;
+ }
}
}
DE_ASSERT((dstAccesses & (~m_allowedAccesses)) == 0);
}
+void CacheState::checkImageLayoutBarrier (vk::VkPipelineStageFlags srcStages,
+ vk::VkAccessFlags srcAccesses,
+ vk::VkPipelineStageFlags dstStages,
+ vk::VkAccessFlags dstAccesses)
+{
+ DE_ASSERT((srcStages & (~m_allowedStages)) == 0);
+ DE_ASSERT((srcAccesses & (~m_allowedAccesses)) == 0);
+ DE_ASSERT((dstStages & (~m_allowedStages)) == 0);
+ DE_ASSERT((dstAccesses & (~m_allowedAccesses)) == 0);
+
+ DE_UNREF(srcStages);
+ DE_UNREF(srcAccesses);
+
+ DE_UNREF(dstStages);
+ DE_UNREF(dstAccesses);
+
+#if defined(DE_DEBUG)
+ // Check that all stages have completed before srcStages or are in srcStages.
+ {
+ vk::VkPipelineStageFlags completedStages = srcStages;
+
+ for (vk::VkPipelineStageFlags srcStage_ = 1; srcStage_ <= srcStages; srcStage_ <<= 1)
+ {
+ const PipelineStage srcStage = pipelineStageFlagToPipelineStage((vk::VkPipelineStageFlagBits)srcStage_);
+
+ if ((srcStage_ & srcStages) == 0)
+ continue;
+
+ completedStages |= (~m_incompleteOperations[srcStage]);
+ }
+
+ DE_ASSERT((completedStages & m_allowedStages) == m_allowedStages);
+ }
+
+ // Check that any write is available at least in one stage. Since all stages are complete even single flush is enough.
+ if ((getWriteAccessFlags() & m_allowedAccesses) != 0 && (srcAccesses & getWriteAccessFlags()) == 0)
+ {
+ bool anyWriteAvailable = false;
+
+ for (vk::VkPipelineStageFlags dstStage_ = 1; dstStage_ <= m_allowedStages; dstStage_ <<= 1)
+ {
+ const PipelineStage dstStage = pipelineStageFlagToPipelineStage((vk::VkPipelineStageFlagBits)dstStage_);
+
+ if ((dstStage_ & m_allowedStages) == 0)
+ continue;
+
+ for (vk::VkPipelineStageFlags srcStage_ = 1; srcStage_ <= m_allowedStages; srcStage_ <<= 1)
+ {
+ const PipelineStage srcStage = pipelineStageFlagToPipelineStage((vk::VkPipelineStageFlagBits)srcStage_);
+
+ if ((srcStage_ & m_allowedStages) == 0)
+ continue;
+
+ if (m_unavailableWriteOperations[dstStage][srcStage] != (getWriteAccessFlags() & m_allowedAccesses))
+ {
+ anyWriteAvailable = true;
+ break;
+ }
+ }
+ }
+
+ DE_ASSERT(anyWriteAvailable);
+ }
+#endif
+}
+
+void CacheState::imageLayoutBarrier (vk::VkPipelineStageFlags srcStages,
+ vk::VkAccessFlags srcAccesses,
+ vk::VkPipelineStageFlags dstStages,
+ vk::VkAccessFlags dstAccesses)
+{
+ checkImageLayoutBarrier(srcStages, srcAccesses, dstStages, dstAccesses);
+
+ for (vk::VkPipelineStageFlags dstStage_ = 1; dstStage_ <= m_allowedStages; dstStage_ <<= 1)
+ {
+ const PipelineStage dstStage = pipelineStageFlagToPipelineStage((vk::VkPipelineStageFlagBits)dstStage_);
+
+ if ((dstStage_ & m_allowedStages) == 0)
+ continue;
+
+ // All stages are incomplete after the barrier except each dstStage in it self.
+ m_incompleteOperations[dstStage] = m_allowedStages & (~dstStage_);
+
+ // All memory operations are invisible unless they are listed in dstAccess
+ m_invisibleOperations[dstStage] = m_allowedAccesses & (~dstAccesses);
+
+ // Layout transition is unavailable in stage unless it was listed in dstStages
+ m_unavailableLayoutTransition[dstStage]= (dstStage_ & dstStages) == 0;
+
+ for (vk::VkPipelineStageFlags srcStage_ = 1; srcStage_ <= m_allowedStages; srcStage_ <<= 1)
+ {
+ const PipelineStage srcStage = pipelineStageFlagToPipelineStage((vk::VkPipelineStageFlagBits)srcStage_);
+
+ if ((srcStage_ & m_allowedStages) == 0)
+ continue;
+
+ // All write operations are available after layout transition
+ m_unavailableWriteOperations[dstStage][srcStage] = 0;
+ }
+ }
+}
+
void CacheState::barrier (vk::VkPipelineStageFlags srcStages,
vk::VkAccessFlags srcAccesses,
vk::VkPipelineStageFlags dstStages,
{
vk::VkPipelineStageFlags oldIncompleteOperations[PIPELINESTAGE_LAST];
vk::VkAccessFlags oldUnavailableWriteOperations[PIPELINESTAGE_LAST][PIPELINESTAGE_LAST];
+ bool oldUnavailableLayoutTransition[PIPELINESTAGE_LAST];
deMemcpy(oldIncompleteOperations, m_incompleteOperations, sizeof(oldIncompleteOperations));
deMemcpy(oldUnavailableWriteOperations, m_unavailableWriteOperations, sizeof(oldUnavailableWriteOperations));
+ deMemcpy(oldUnavailableLayoutTransition, m_unavailableLayoutTransition, sizeof(oldUnavailableLayoutTransition));
for (vk::VkPipelineStageFlags srcStage_ = 1; srcStage_ <= srcStages; srcStage_ <<= 1)
{
// Stages that have completed before srcStage have also completed before dstStage
m_incompleteOperations[dstStage] &= oldIncompleteOperations[srcStage];
+ // Image layout transition in srcStage are now available in dstStage
+ m_unavailableLayoutTransition[dstStage] &= oldUnavailableLayoutTransition[srcStage];
+
for (vk::VkPipelineStageFlags sharedStage_ = 1; sharedStage_ <= m_allowedStages; sharedStage_ <<= 1)
{
const PipelineStage sharedStage = pipelineStageFlagToPipelineStage((vk::VkPipelineStageFlagBits)sharedStage_);
if (m_incompleteOperations[dstStage] != 0)
return false;
+ // Layout transition has not completed yet
+ if (m_unavailableLayoutTransition[dstStage])
+ return false;
+
for (vk::VkPipelineStageFlags srcStage_ = 1; srcStage_ <= m_allowedStages; srcStage_ <<= 1)
{
const PipelineStage srcStage = pipelineStageFlagToPipelineStage((vk::VkPipelineStageFlagBits)srcStage_);
return true;
}
-void CacheState::fullBarrier (void)
-{
- for (vk::VkPipelineStageFlags dstStage_ = 1; dstStage_ <= m_allowedStages; dstStage_ <<= 1)
- {
- const PipelineStage dstStage = pipelineStageFlagToPipelineStage((vk::VkPipelineStageFlagBits)dstStage_);
-
- if ((dstStage_ & m_allowedStages) == 0)
- continue;
-
- // All stages have completed
- m_incompleteOperations[dstStage] = 0;
-
- // All operations are visible
- m_invisibleOperations[dstStage] = 0;
-
- for (vk::VkPipelineStageFlags srcStage_ = 1; srcStage_ <= m_allowedStages; srcStage_ <<= 1)
- {
- const PipelineStage srcStage = pipelineStageFlagToPipelineStage((vk::VkPipelineStageFlagBits)srcStage_);
-
- if ((srcStage_ & m_allowedStages) == 0)
- continue;
-
- // All writes are available
- m_unavailableWriteOperations[dstStage][srcStage] = 0;
- }
- }
-}
-
struct State
{
State (Usage usage, deUint32 seed)
, hasBoundBufferMemory (false)
, hasImage (false)
, hasBoundImageMemory (false)
- , imageHasGeneralLayout (false)
+ , imageLayout (vk::VK_IMAGE_LAYOUT_UNDEFINED)
, imageDefined (false)
, queueIdle (true)
, deviceIdle (true)
{
}
- Stage stage;
- CacheState cache;
- de::Random rng;
+ Stage stage;
+ CacheState cache;
+ de::Random rng;
- bool mapped;
- bool hostInvalidated;
- bool hostFlushed;
- bool memoryDefined;
+ bool mapped;
+ bool hostInvalidated;
+ bool hostFlushed;
+ bool memoryDefined;
- bool hasBuffer;
- bool hasBoundBufferMemory;
+ bool hasBuffer;
+ bool hasBoundBufferMemory;
- bool hasImage;
- bool hasBoundImageMemory;
- bool imageHasGeneralLayout;
- bool imageDefined;
+ bool hasImage;
+ bool hasBoundImageMemory;
+ vk::VkImageLayout imageLayout;
+ bool imageDefined;
- bool queueIdle;
- bool deviceIdle;
+ bool queueIdle;
+ bool deviceIdle;
- bool commandBufferIsEmpty;
+ bool commandBufferIsEmpty;
};
void getAvailableOps (const State& state, bool supportsBuffers, bool supportsImages, Usage usage, vector<Op>& ops)
if (state.hasBoundImageMemory)
{
- if (!state.imageHasGeneralLayout)
- {
- ops.push_back(OP_IMAGE_TRANSITION_TO_GENERAL);
- }
- else
+ ops.push_back(OP_IMAGE_TRANSITION_LAYOUT);
+
{
if (usage & USAGE_TRANSFER_DST
- && state.imageHasGeneralLayout
+ && (state.imageLayout == vk::VK_IMAGE_LAYOUT_GENERAL
+ || state.imageLayout == vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
&& state.cache.isValid(vk::VK_PIPELINE_STAGE_TRANSFER_BIT, vk::VK_ACCESS_TRANSFER_WRITE_BIT))
{
ops.push_back(OP_IMAGE_COPY_FROM_BUFFER);
}
if (usage & USAGE_TRANSFER_SRC
- && state.imageHasGeneralLayout
+ && (state.imageLayout == vk::VK_IMAGE_LAYOUT_GENERAL
+ || state.imageLayout == vk::VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
&& state.imageDefined
&& state.cache.isValid(vk::VK_PIPELINE_STAGE_TRANSFER_BIT, vk::VK_ACCESS_TRANSFER_READ_BIT))
{
}
}
- // \todo Add other usages?
+ // \todo [2016-03-09 mika] Add other usages?
if (((usage & USAGE_VERTEX_BUFFER) && state.cache.isValid(vk::VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, vk::VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT))
|| ((usage & USAGE_INDEX_BUFFER) && state.cache.isValid(vk::VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, vk::VK_ACCESS_INDEX_READ_BIT)))
ops.push_back(OP_RENDERPASS_BEGIN);
DE_FATAL("Unknown stage");
}
-void applyOp (State& state, const Memory& memory, Op op)
+bool layoutSupportedByUsage (Usage usage, vk::VkImageLayout layout)
+{
+ switch (layout)
+ {
+ case vk::VK_IMAGE_LAYOUT_GENERAL:
+ return true;
+
+ case vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
+ return (usage & USAGE_COLOR_ATTACHMENT) != 0;
+
+ case vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
+ return (usage & USAGE_DEPTH_STENCIL_ATTACHMENT) != 0;
+
+ case vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
+ return (usage & USAGE_DEPTH_STENCIL_ATTACHMENT) != 0;
+
+ case vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
+ // \todo [2016-03-09 mika] Should include input attachment
+ return (usage & USAGE_TEXTURE_SAMPLED) != 0;
+
+ case vk::VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
+ return (usage & USAGE_TRANSFER_SRC) != 0;
+
+ case vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
+ return (usage & USAGE_TRANSFER_DST) != 0;
+
+ case vk::VK_IMAGE_LAYOUT_PREINITIALIZED:
+ return true;
+
+ default:
+ DE_FATAL("Unknown layout");
+ return false;
+ }
+}
+
+vk::VkImageLayout getRandomNextLayout (de::Random& rng,
+ Usage usage,
+ vk::VkImageLayout previousLayout)
+{
+ const vk::VkImageLayout layouts[] =
+ {
+ vk::VK_IMAGE_LAYOUT_GENERAL,
+ vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
+ vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL,
+ vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ vk::VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ };
+ size_t possibleLayoutCount = 0;
+
+ for (size_t layoutNdx = 0; layoutNdx < DE_LENGTH_OF_ARRAY(layouts); layoutNdx++)
+ {
+ const vk::VkImageLayout layout = layouts[layoutNdx];
+
+ if (layoutSupportedByUsage(usage, layout) && layout != previousLayout)
+ possibleLayoutCount++;
+ }
+
+ size_t nextLayoutNdx = ((size_t)rng.getUint64()) % possibleLayoutCount;
+
+ for (size_t layoutNdx = 0; layoutNdx < DE_LENGTH_OF_ARRAY(layouts); layoutNdx++)
+ {
+ const vk::VkImageLayout layout = layouts[layoutNdx];
+
+ if (layoutSupportedByUsage(usage, layout) && layout != previousLayout)
+ {
+ if (nextLayoutNdx == 0)
+ return layout;
+ else
+ nextLayoutNdx--;
+ }
+ }
+
+ DE_FATAL("Unreachable");
+ return vk::VK_IMAGE_LAYOUT_UNDEFINED;
+}
+
+void applyOp (State& state, const Memory& memory, Op op, Usage usage)
{
switch (op)
{
state.memoryDefined = true;
state.imageDefined = false;
- state.imageHasGeneralLayout = false;
+ state.imageLayout = vk::VK_IMAGE_LAYOUT_UNDEFINED;
state.rng.getUint32();
break;
state.hasImage = false;
state.hasBoundImageMemory = false;
- state.imageHasGeneralLayout = false;
+ state.imageLayout = vk::VK_IMAGE_LAYOUT_UNDEFINED;
state.imageDefined = false;
break;
state.hasBoundImageMemory = true;
break;
- case OP_IMAGE_TRANSITION_TO_GENERAL:
+ case OP_IMAGE_TRANSITION_LAYOUT:
+ {
DE_ASSERT(state.stage == STAGE_COMMAND_BUFFER);
DE_ASSERT(state.hasImage);
DE_ASSERT(state.hasBoundImageMemory);
- state.imageHasGeneralLayout = true;
+ // \todo [2016-03-09 mika] Support linear tiling and predefined data
+ const vk::VkImageLayout srcLayout = state.rng.getFloat() < 0.9f ? state.imageLayout : vk::VK_IMAGE_LAYOUT_UNDEFINED;
+ const vk::VkImageLayout dstLayout = getRandomNextLayout(state.rng, usage, srcLayout);
+
+ vk::VkPipelineStageFlags dirtySrcStages;
+ vk::VkAccessFlags dirtySrcAccesses;
+ vk::VkPipelineStageFlags dirtyDstStages;
+ vk::VkAccessFlags dirtyDstAccesses;
+
+ vk::VkPipelineStageFlags srcStages;
+ vk::VkAccessFlags srcAccesses;
+ vk::VkPipelineStageFlags dstStages;
+ vk::VkAccessFlags dstAccesses;
+
+ state.cache.getFullBarrier(dirtySrcStages, dirtySrcAccesses, dirtyDstStages, dirtyDstAccesses);
+
+ // Try masking some random bits
+ srcStages = dirtySrcStages;
+ srcAccesses = dirtySrcAccesses;
+
+ dstStages = state.cache.getAllowedStages() & state.rng.getUint32();
+ dstAccesses = state.cache.getAllowedAcceses() & state.rng.getUint32();
+
+ // If there are no bits in dst stage mask use all stages
+ dstStages = dstStages ? dstStages : state.cache.getAllowedStages();
+
+ if (!srcStages)
+ srcStages = dstStages;
+
+ if (srcLayout == vk::VK_IMAGE_LAYOUT_UNDEFINED)
+ state.imageDefined = false;
+
+ state.commandBufferIsEmpty = false;
+ state.imageLayout = dstLayout;
state.memoryDefined = false;
+ state.cache.imageLayoutBarrier(srcStages, srcAccesses, dstStages, dstAccesses);
break;
+ }
case OP_QUEUE_WAIT_FOR_IDLE:
DE_ASSERT(state.stage == STAGE_HOST);
DE_ASSERT(!state.queueIdle);
state.queueIdle = true;
+
+ state.cache.waitForIdle();
break;
case OP_DEVICE_WAIT_FOR_IDLE:
state.queueIdle = true;
state.deviceIdle = true;
+
+ state.cache.waitForIdle();
break;
case OP_COMMAND_BUFFER_BEGIN:
state.stage = STAGE_HOST;
state.queueIdle = false;
state.deviceIdle = false;
- // \todo Should this set all device reads ready?
break;
case OP_BUFFER_COPY_FROM_BUFFER:
state.commandBufferIsEmpty = false;
state.memoryDefined = true;
state.imageDefined = false;
- state.imageHasGeneralLayout = false;
+ state.imageLayout = vk::VK_IMAGE_LAYOUT_UNDEFINED;
state.cache.perform(vk::VK_PIPELINE_STAGE_TRANSFER_BIT, vk::VK_ACCESS_TRANSFER_WRITE_BIT);
break;
de::MovePtr<CmdCommand> createCmdCommand (de::Random& rng,
const State& state,
- Op op)
+ Op op,
+ Usage usage)
{
switch (op)
{
case OP_BUFFER_COPY_TO_IMAGE: return de::MovePtr<CmdCommand>(new BufferCopyToImage());
case OP_BUFFER_COPY_FROM_IMAGE: return de::MovePtr<CmdCommand>(new BufferCopyFromImage(rng.getUint32()));
- case OP_IMAGE_TRANSITION_TO_GENERAL: return de::MovePtr<CmdCommand>(new ImageTransition());
+ case OP_IMAGE_TRANSITION_LAYOUT:
+ {
+ DE_ASSERT(state.stage == STAGE_COMMAND_BUFFER);
+ DE_ASSERT(state.hasImage);
+ DE_ASSERT(state.hasBoundImageMemory);
+
+ const vk::VkImageLayout srcLayout = rng.getFloat() < 0.9f ? state.imageLayout : vk::VK_IMAGE_LAYOUT_UNDEFINED;
+ const vk::VkImageLayout dstLayout = getRandomNextLayout(rng, usage, srcLayout);
+
+ vk::VkPipelineStageFlags dirtySrcStages;
+ vk::VkAccessFlags dirtySrcAccesses;
+ vk::VkPipelineStageFlags dirtyDstStages;
+ vk::VkAccessFlags dirtyDstAccesses;
+
+ vk::VkPipelineStageFlags srcStages;
+ vk::VkAccessFlags srcAccesses;
+ vk::VkPipelineStageFlags dstStages;
+ vk::VkAccessFlags dstAccesses;
+
+ state.cache.getFullBarrier(dirtySrcStages, dirtySrcAccesses, dirtyDstStages, dirtyDstAccesses);
+
+ // Try masking some random bits
+ srcStages = dirtySrcStages;
+ srcAccesses = dirtySrcAccesses;
+
+ dstStages = state.cache.getAllowedStages() & rng.getUint32();
+ dstAccesses = state.cache.getAllowedAcceses() & rng.getUint32();
+
+ // If there are no bits in dst stage mask use all stages
+ dstStages = dstStages ? dstStages : state.cache.getAllowedStages();
+
+ if (!srcStages)
+ srcStages = dstStages;
+
+ return de::MovePtr<CmdCommand>(new ImageTransition(srcStages, srcAccesses, dstStages, dstAccesses, srcLayout, dstLayout));
+ }
case OP_IMAGE_COPY_TO_BUFFER: return de::MovePtr<CmdCommand>(new ImageCopyToBuffer());
case OP_IMAGE_COPY_FROM_BUFFER: return de::MovePtr<CmdCommand>(new ImageCopyFromBuffer(rng.getUint32()));
size_t& opNdx,
size_t opCount)
{
- // \todo Exception safety
vector<RenderPassCommand*> commands;
- for (; opNdx < opCount; opNdx++)
+ try
{
- vector<Op> ops;
-
- getAvailableOps(state, memory.getSupportBuffers(), memory.getSupportImages(), usage, ops);
+ for (; opNdx < opCount; opNdx++)
+ {
+ vector<Op> ops;
- DE_ASSERT(!ops.empty());
+ getAvailableOps(state, memory.getSupportBuffers(), memory.getSupportImages(), usage, ops);
- {
- const Op op = nextOpRng.choose<Op>(ops.begin(), ops.end());
+ DE_ASSERT(!ops.empty());
- if (op == OP_RENDERPASS_END)
{
- break;
- }
- else
- {
- de::Random rng (state.rng);
+ const Op op = nextOpRng.choose<Op>(ops.begin(), ops.end());
- commands.push_back(createRenderPassCommand(rng, state, op).release());
- applyOp(state, memory, op);
+ if (op == OP_RENDERPASS_END)
+ {
+ break;
+ }
+ else
+ {
+ de::Random rng (state.rng);
- DE_ASSERT(state.rng == rng);
+ commands.push_back(createRenderPassCommand(rng, state, op).release());
+ applyOp(state, memory, op, usage);
+
+ DE_ASSERT(state.rng == rng);
+ }
}
}
+
+ applyOp(state, memory, OP_RENDERPASS_END, usage);
+ return de::MovePtr<CmdCommand>(new SubmitRenderPass(commands));
}
+ catch (...)
+ {
+ for (size_t commandNdx = 0; commandNdx < commands.size(); commandNdx++)
+ delete commands[commandNdx];
- applyOp(state, memory, OP_RENDERPASS_END);
- return de::MovePtr<CmdCommand>(new SubmitRenderPass(commands));
+ throw;
+ }
}
de::MovePtr<Command> createCmdCommands (const Memory& memory,
size_t& opNdx,
size_t opCount)
{
- // \todo Exception safety
vector<CmdCommand*> commands;
- for (; opNdx < opCount; opNdx++)
+ try
{
- vector<Op> ops;
+ for (; opNdx < opCount; opNdx++)
+ {
+ vector<Op> ops;
- getAvailableOps(state, memory.getSupportBuffers(), memory.getSupportImages(), usage, ops);
+ getAvailableOps(state, memory.getSupportBuffers(), memory.getSupportImages(), usage, ops);
- DE_ASSERT(!ops.empty());
+ DE_ASSERT(!ops.empty());
- {
- const Op op = nextOpRng.choose<Op>(ops.begin(), ops.end());
-
- if (op == OP_COMMAND_BUFFER_END)
- {
- break;
- }
- else
{
- // \note Command needs to known the state before the operation
- if (op == OP_RENDERPASS_BEGIN)
+ const Op op = nextOpRng.choose<Op>(ops.begin(), ops.end());
+
+ if (op == OP_COMMAND_BUFFER_END)
{
- applyOp(state, memory, op);
- commands.push_back(createRenderPassCommands(memory, nextOpRng, state, usage, opNdx, opCount).release());
+ break;
}
else
{
- de::Random rng (state.rng);
+ // \note Command needs to known the state before the operation
+ if (op == OP_RENDERPASS_BEGIN)
+ {
+ applyOp(state, memory, op, usage);
+ commands.push_back(createRenderPassCommands(memory, nextOpRng, state, usage, opNdx, opCount).release());
+ }
+ else
+ {
+ de::Random rng (state.rng);
- commands.push_back(createCmdCommand(rng, state, op).release());
- applyOp(state, memory, op);
+ commands.push_back(createCmdCommand(rng, state, op, usage).release());
+ applyOp(state, memory, op, usage);
- DE_ASSERT(state.rng == rng);
- }
+ DE_ASSERT(state.rng == rng);
+ }
+ }
}
}
+
+ applyOp(state, memory, OP_COMMAND_BUFFER_END, usage);
+ return de::MovePtr<Command>(new SubmitCommandBuffer(commands));
}
+ catch (...)
+ {
+ for (size_t commandNdx = 0; commandNdx < commands.size(); commandNdx++)
+ delete commands[commandNdx];
- applyOp(state, memory, OP_COMMAND_BUFFER_END);
- return de::MovePtr<Command>(new SubmitCommandBuffer(commands));
+ throw;
+ }
}
void createCommands (vector<Command*>& commands,
if (op == OP_COMMAND_BUFFER_BEGIN)
{
- applyOp(state, memory, op);
+ applyOp(state, memory, op, usage);
commands.push_back(createCmdCommands(memory, nextOpRng, state, usage, opNdx, opCount).release());
}
else
de::Random rng (state.rng);
commands.push_back(createHostCommand(op, rng, usage, sharingMode).release());
- applyOp(state, memory, op);
+ applyOp(state, memory, op, usage);
// Make sure that random generator is in sync
DE_ASSERT(state.rng == rng);
tcu::TestStatus MemoryTestInstance::iterate (void)
{
- // \todo Split different stages over multiple iterations
+ // \todo [2016-03-09 mika] Split different stages over multiple iterations
if (m_memoryTypeNdx < m_memoryProperties.memoryTypeCount)
{
TestLog& log = m_context.getTestContext().getLog();
#include "vkTypeUtil.hpp"
#include "vkWsiPlatform.hpp"
#include "vkWsiUtil.hpp"
+#include "vkAllocationCallbackUtil.hpp"
#include "tcuTestLog.hpp"
#include "tcuFormatUtil.hpp"
}
}
-Move<VkInstance> createInstanceWithWsi (const PlatformInterface& vkp,
- const Extensions& supportedExtensions,
- Type wsiType)
+Move<VkInstance> createInstanceWithWsi (const PlatformInterface& vkp,
+ const Extensions& supportedExtensions,
+ Type wsiType,
+ const VkAllocationCallbacks* pAllocator = DE_NULL)
{
vector<string> extensions;
checkAllSupported(supportedExtensions, extensions);
- return createDefaultInstance(vkp, vector<string>(), extensions);
+ return createDefaultInstance(vkp, vector<string>(), extensions, pAllocator);
}
struct InstanceHelper
Unique<VkInstance> instance;
const InstanceDriver vki;
- InstanceHelper (Context& context, Type wsiType)
+ InstanceHelper (Context& context, Type wsiType, const VkAllocationCallbacks* pAllocator = DE_NULL)
: supportedExtensions (enumerateInstanceExtensionProperties(context.getPlatformInterface(),
DE_NULL))
, instance (createInstanceWithWsi(context.getPlatformInterface(),
supportedExtensions,
- wsiType))
+ wsiType,
+ pAllocator))
, vki (context.getPlatformInterface(), *instance)
{}
};
return tcu::TestStatus::pass("Creating surface succeeded");
}
+tcu::TestStatus createSurfaceCustomAllocatorTest (Context& context, Type wsiType)
+{
+ AllocationCallbackRecorder allocationRecorder (getSystemAllocator());
+ tcu::TestLog& log = context.getTestContext().getLog();
+
+ {
+ const InstanceHelper instHelper (context, wsiType, allocationRecorder.getCallbacks());
+ const NativeObjects native (context, instHelper.supportedExtensions, wsiType);
+ const Unique<VkSurfaceKHR> surface (createSurface(instHelper.vki,
+ *instHelper.instance,
+ wsiType,
+ *native.display,
+ *native.window,
+ allocationRecorder.getCallbacks()));
+
+ if (!validateAndLog(log,
+ allocationRecorder,
+ (1u<<VK_SYSTEM_ALLOCATION_SCOPE_OBJECT) |
+ (1u<<VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE)))
+ return tcu::TestStatus::fail("Detected invalid system allocation callback");
+ }
+
+ if (!validateAndLog(log, allocationRecorder, 0u))
+ return tcu::TestStatus::fail("Detected invalid system allocation callback");
+
+ if (allocationRecorder.getRecordsBegin() == allocationRecorder.getRecordsEnd())
+ return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Allocation callbacks were not used");
+ else
+ return tcu::TestStatus::pass("Creating surface succeeded using custom allocator");
+}
+
+tcu::TestStatus createSurfaceSimulateOOMTest (Context& context, Type wsiType)
+{
+ tcu::TestLog& log = context.getTestContext().getLog();
+
+ for (deUint32 numPassingAllocs = 0; numPassingAllocs <= 1024u; ++numPassingAllocs)
+ {
+ AllocationCallbackRecorder allocationRecorder (getSystemAllocator());
+ DeterministicFailAllocator failingAllocator (allocationRecorder.getCallbacks(), numPassingAllocs);
+ bool gotOOM = false;
+
+ log << TestLog::Message << "Testing with " << numPassingAllocs << " first allocations succeeding" << TestLog::EndMessage;
+
+ try
+ {
+ const InstanceHelper instHelper (context, wsiType, failingAllocator.getCallbacks());
+ const NativeObjects native (context, instHelper.supportedExtensions, wsiType);
+ const Unique<VkSurfaceKHR> surface (createSurface(instHelper.vki,
+ *instHelper.instance,
+ wsiType,
+ *native.display,
+ *native.window,
+ failingAllocator.getCallbacks()));
+
+ if (!validateAndLog(log,
+ allocationRecorder,
+ (1u<<VK_SYSTEM_ALLOCATION_SCOPE_OBJECT) |
+ (1u<<VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE)))
+ return tcu::TestStatus::fail("Detected invalid system allocation callback");
+ }
+ catch (const OutOfMemoryError& e)
+ {
+ log << TestLog::Message << "Got " << e.getError() << TestLog::EndMessage;
+ gotOOM = true;
+ }
+
+ if (!validateAndLog(log, allocationRecorder, 0u))
+ return tcu::TestStatus::fail("Detected invalid system allocation callback");
+
+ if (!gotOOM)
+ {
+ log << TestLog::Message << "Creating surface succeeded!" << TestLog::EndMessage;
+
+ if (numPassingAllocs == 0)
+ return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Allocation callbacks were not used");
+ else
+ return tcu::TestStatus::pass("OOM simulation completed");
+ }
+ }
+
+ return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Creating surface did not succeed, callback limit exceeded");
+}
+
deUint32 getNumQueueFamilies (const InstanceInterface& vki, VkPhysicalDevice physicalDevice)
{
deUint32 numFamilies = 0;
{
const PlatformProperties& platformProperties = getPlatformProperties(wsiType);
- addFunctionCase(testGroup, "create", "Create surface", createSurfaceTest, wsiType);
- addFunctionCase(testGroup, "query_support", "Query surface support", querySurfaceSupportTest, wsiType);
- addFunctionCase(testGroup, "query_capabilities", "Query surface capabilities", querySurfaceCapabilitiesTest, wsiType);
- addFunctionCase(testGroup, "query_formats", "Query surface formats", querySurfaceFormatsTest, wsiType);
- addFunctionCase(testGroup, "query_present_modes", "Query surface present modes", querySurfacePresentModesTest, wsiType);
+ addFunctionCase(testGroup, "create", "Create surface", createSurfaceTest, wsiType);
+ addFunctionCase(testGroup, "create_custom_allocator", "Create surface with custom allocator", createSurfaceCustomAllocatorTest, wsiType);
+ addFunctionCase(testGroup, "create_simulate_oom", "Create surface with simulating OOM", createSurfaceSimulateOOMTest, wsiType);
+ addFunctionCase(testGroup, "query_support", "Query surface support", querySurfaceSupportTest, wsiType);
+ addFunctionCase(testGroup, "query_capabilities", "Query surface capabilities", querySurfaceCapabilitiesTest, wsiType);
+ addFunctionCase(testGroup, "query_formats", "Query surface formats", querySurfaceFormatsTest, wsiType);
+ addFunctionCase(testGroup, "query_present_modes", "Query surface present modes", querySurfacePresentModesTest, wsiType);
if ((platformProperties.features & PlatformProperties::FEATURE_INITIAL_WINDOW_SIZE) != 0)
addFunctionCase(testGroup, "initial_size", "Create surface with initial window size set", createSurfaceInitialSizeTest, wsiType);
#include "vkTypeUtil.hpp"
#include "vkWsiPlatform.hpp"
#include "vkWsiUtil.hpp"
+#include "vkAllocationCallbackUtil.hpp"
#include "tcuTestLog.hpp"
#include "tcuFormatUtil.hpp"
}
}
-Move<VkInstance> createInstanceWithWsi (const PlatformInterface& vkp,
- const Extensions& supportedExtensions,
- Type wsiType)
+Move<VkInstance> createInstanceWithWsi (const PlatformInterface& vkp,
+ const Extensions& supportedExtensions,
+ Type wsiType,
+ const VkAllocationCallbacks* pAllocator = DE_NULL)
{
vector<string> extensions;
checkAllSupported(supportedExtensions, extensions);
- return createDefaultInstance(vkp, vector<string>(), extensions);
+ return createDefaultInstance(vkp, vector<string>(), extensions, pAllocator);
}
VkPhysicalDeviceFeatures getDeviceFeaturesForWsi (void)
return features;
}
-Move<VkDevice> createDeviceWithWsi (const InstanceInterface& vki,
- VkPhysicalDevice physicalDevice,
- const Extensions& supportedExtensions,
- const deUint32 queueFamilyIndex)
+Move<VkDevice> createDeviceWithWsi (const InstanceInterface& vki,
+ VkPhysicalDevice physicalDevice,
+ const Extensions& supportedExtensions,
+ const deUint32 queueFamilyIndex,
+ const VkAllocationCallbacks* pAllocator = DE_NULL)
{
const float queuePriorities[] = { 1.0f };
const VkDeviceQueueCreateInfo queueInfos[] =
TCU_THROW(NotSupportedError, (string(extensions[ndx]) + " is not supported").c_str());
}
- return createDevice(vki, physicalDevice, &deviceParams, (const VkAllocationCallbacks*)DE_NULL);
+ return createDevice(vki, physicalDevice, &deviceParams, pAllocator);
}
deUint32 getNumQueueFamilyIndices (const InstanceInterface& vki, VkPhysicalDevice physicalDevice)
const Unique<VkInstance> instance;
const InstanceDriver vki;
- InstanceHelper (Context& context, Type wsiType)
+ InstanceHelper (Context& context, Type wsiType, const VkAllocationCallbacks* pAllocator = DE_NULL)
: supportedExtensions (enumerateInstanceExtensionProperties(context.getPlatformInterface(),
DE_NULL))
, instance (createInstanceWithWsi(context.getPlatformInterface(),
supportedExtensions,
- wsiType))
+ wsiType,
+ pAllocator))
, vki (context.getPlatformInterface(), *instance)
{}
};
const DeviceDriver vkd;
const VkQueue queue;
- DeviceHelper (Context& context, const InstanceInterface& vki, VkInstance instance, VkSurfaceKHR surface)
+ DeviceHelper (Context& context,
+ const InstanceInterface& vki,
+ VkInstance instance,
+ VkSurfaceKHR surface,
+ const VkAllocationCallbacks* pAllocator = DE_NULL)
: physicalDevice (chooseDevice(vki, instance, context.getTestContext().getCommandLine()))
, queueFamilyIndex (chooseQueueFamilyIndex(vki, physicalDevice, surface))
, device (createDeviceWithWsi(vki,
physicalDevice,
enumerateDeviceExtensionProperties(vki, physicalDevice, DE_NULL),
- queueFamilyIndex))
+ queueFamilyIndex,
+ pAllocator))
, vkd (vki, *device)
, queue (getDeviceQueue(vkd, *device, queueFamilyIndex, 0))
{
return tcu::TestStatus::pass("Creating swapchain succeeded");
}
+tcu::TestStatus createSwapchainSimulateOOMTest (Context& context, TestParameters params)
+{
+ tcu::TestLog& log = context.getTestContext().getLog();
+
+ // \note This is a little counter-intuitive order (iterating on callback count until all cases pass)
+ // but since cases depend on what device reports, it is the only easy way. In practice
+ // we should see same number of total callbacks (and executed code) regardless of the
+ // loop order.
+
+ for (deUint32 numPassingAllocs = 0; numPassingAllocs <= 16*1024u; ++numPassingAllocs)
+ {
+ AllocationCallbackRecorder allocationRecorder (getSystemAllocator());
+ DeterministicFailAllocator failingAllocator (allocationRecorder.getCallbacks(), numPassingAllocs);
+ bool gotOOM = false;
+
+ log << TestLog::Message << "Testing with " << numPassingAllocs << " first allocations succeeding" << TestLog::EndMessage;
+
+ try
+ {
+ const InstanceHelper instHelper (context, params.wsiType, failingAllocator.getCallbacks());
+ const NativeObjects native (context, instHelper.supportedExtensions, params.wsiType);
+ const Unique<VkSurfaceKHR> surface (createSurface(instHelper.vki,
+ *instHelper.instance,
+ params.wsiType,
+ *native.display,
+ *native.window,
+ failingAllocator.getCallbacks()));
+ const DeviceHelper devHelper (context, instHelper.vki, *instHelper.instance, *surface, failingAllocator.getCallbacks());
+ const vector<VkSwapchainCreateInfoKHR> cases (generateSwapchainParameterCases(params.wsiType, params.dimension, instHelper.vki, devHelper.physicalDevice, *surface));
+
+ for (size_t caseNdx = 0; caseNdx < cases.size(); ++caseNdx)
+ {
+ VkSwapchainCreateInfoKHR curParams = cases[caseNdx];
+
+ curParams.surface = *surface;
+ curParams.queueFamilyIndexCount = 1u;
+ curParams.pQueueFamilyIndices = &devHelper.queueFamilyIndex;
+
+ context.getTestContext().getLog()
+ << TestLog::Message << "Sub-case " << (caseNdx+1) << " / " << cases.size() << TestLog::EndMessage;
+
+ {
+ const Unique<VkSwapchainKHR> swapchain (createSwapchainKHR(devHelper.vkd, *devHelper.device, &curParams, failingAllocator.getCallbacks()));
+ }
+ }
+ }
+ catch (const OutOfMemoryError& e)
+ {
+ log << TestLog::Message << "Got " << e.getError() << TestLog::EndMessage;
+ gotOOM = true;
+ }
+
+ if (!validateAndLog(log, allocationRecorder, 0u))
+ return tcu::TestStatus::fail("Detected invalid system allocation callback");
+
+ if (!gotOOM)
+ {
+ log << TestLog::Message << "Creating surface succeeded!" << TestLog::EndMessage;
+
+ if (numPassingAllocs == 0)
+ return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Allocation callbacks were not used");
+ else
+ return tcu::TestStatus::pass("OOM simulation completed");
+ }
+ }
+
+ return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Creating swapchain did not succeed, callback limit exceeded");
+}
+
struct GroupParameters
{
typedef FunctionInstance1<TestParameters>::Function Function;
void createSwapchainTests (tcu::TestCaseGroup* testGroup, vk::wsi::Type wsiType)
{
- addTestGroup(testGroup, "create", "Create VkSwapchain with various parameters", populateSwapchainGroup, GroupParameters(wsiType, createSwapchainTest));
+ addTestGroup(testGroup, "create", "Create VkSwapchain with various parameters", populateSwapchainGroup, GroupParameters(wsiType, createSwapchainTest));
+ addTestGroup(testGroup, "simulate_oom", "Simulate OOM using callbacks during swapchain construction", populateSwapchainGroup, GroupParameters(wsiType, createSwapchainSimulateOOMTest));
}
} // wsi