layers: Fix Graphics Pipeline pointers not ignored
Some VkGraphicsPipelineCreateInfo pointers must be ignored under some
conditions, but were not in the layers.
Add relevant tests.
Fix tests found broken (using depth or color without attachment in
subpass)
diff --git a/layers/core_validation.cpp b/layers/core_validation.cpp
index 01040e4..42f656e 100644
--- a/layers/core_validation.cpp
+++ b/layers/core_validation.cpp
@@ -1287,13 +1287,13 @@
// Ensure the subpass index is valid. If not, then validate_and_capture_pipeline_shader_state
// produces nonsense errors that confuse users. Other layers should already
// emit errors for renderpass being invalid.
- auto subpass_desc = &pPipeline->render_pass_ci.pSubpasses[pPipeline->graphicsPipelineCI.subpass];
- if (pPipeline->graphicsPipelineCI.subpass >= pPipeline->render_pass_ci.subpassCount) {
+ auto subpass_desc = &pPipeline->rp_state->createInfo.pSubpasses[pPipeline->graphicsPipelineCI.subpass];
+ if (pPipeline->graphicsPipelineCI.subpass >= pPipeline->rp_state->createInfo.subpassCount) {
skip |= log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT,
HandleToUint64(pPipeline->pipeline), __LINE__, VALIDATION_ERROR_096005ee, "DS",
"Invalid Pipeline CreateInfo State: Subpass index %u "
"is out of range for this renderpass (0..%u). %s",
- pPipeline->graphicsPipelineCI.subpass, pPipeline->render_pass_ci.subpassCount - 1,
+ pPipeline->graphicsPipelineCI.subpass, pPipeline->rp_state->createInfo.subpassCount - 1,
validation_error_map[VALIDATION_ERROR_096005ee]);
subpass_desc = nullptr;
}
@@ -1500,11 +1500,11 @@
VkSampleCountFlagBits max_sample_count = static_cast<VkSampleCountFlagBits>(0);
for (uint32_t i = 0; i < subpass_desc->colorAttachmentCount; ++i) {
if (subpass_desc->pColorAttachments[i].attachment != VK_ATTACHMENT_UNUSED) {
- max_sample_count = std::max(max_sample_count, pPipeline->render_pass_ci.pAttachments[subpass_desc->pColorAttachments[i].attachment].samples);
+ max_sample_count = std::max(max_sample_count, pPipeline->rp_state->createInfo.pAttachments[subpass_desc->pColorAttachments[i].attachment].samples);
}
}
if (subpass_desc->pDepthStencilAttachment && subpass_desc->pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) {
- max_sample_count = std::max(max_sample_count, pPipeline->render_pass_ci.pAttachments[subpass_desc->pDepthStencilAttachment->attachment].samples);
+ max_sample_count = std::max(max_sample_count, pPipeline->rp_state->createInfo.pAttachments[subpass_desc->pDepthStencilAttachment->attachment].samples);
}
if (pPipeline->graphicsPipelineCI.pMultisampleState->rasterizationSamples != max_sample_count) {
skip |=
@@ -4613,7 +4613,6 @@
for (i = 0; i < count; i++) {
pipe_state.push_back(std::unique_ptr<PIPELINE_STATE>(new PIPELINE_STATE));
pipe_state[i]->initGraphicsPipeline(&pCreateInfos[i], GetRenderPassStateSharedPtr(dev_data, pCreateInfos[i].renderPass));
- pipe_state[i]->render_pass_ci.initialize(GetRenderPassState(dev_data, pCreateInfos[i].renderPass)->createInfo.ptr());
pipe_state[i]->pipeline_layout = *getPipelineLayout(dev_data, pCreateInfos[i].layout);
}
diff --git a/layers/core_validation_types.h b/layers/core_validation_types.h
index bc5232b..f661473 100644
--- a/layers/core_validation_types.h
+++ b/layers/core_validation_types.h
@@ -559,8 +559,6 @@
std::vector<VkVertexInputBindingDescription> vertexBindingDescriptions;
std::vector<VkPipelineColorBlendAttachmentState> attachments;
bool blendConstantsEnabled; // Blend constants enabled for any attachments
- // Store RPCI b/c renderPass may be destroyed after Pipeline creation
- safe_VkRenderPassCreateInfo render_pass_ci;
PIPELINE_LAYOUT_NODE pipeline_layout;
// Default constructor
@@ -575,11 +573,26 @@
vertexBindingDescriptions(),
attachments(),
blendConstantsEnabled(false),
- render_pass_ci(),
pipeline_layout() {}
void initGraphicsPipeline(const VkGraphicsPipelineCreateInfo *pCreateInfo, std::shared_ptr<RENDER_PASS_STATE> &&rpstate) {
- graphicsPipelineCI.initialize(pCreateInfo);
+ bool uses_color_attachment = false;
+ bool uses_depthstencil_attachment = false;
+ if (pCreateInfo->subpass < rpstate->createInfo.subpassCount) {
+ const auto &subpass = rpstate->createInfo.pSubpasses[pCreateInfo->subpass];
+
+ for (uint32_t i = 0; i < subpass.colorAttachmentCount; ++i) {
+ if (subpass.pColorAttachments[i].attachment != VK_ATTACHMENT_UNUSED) {
+ uses_color_attachment = true;
+ break;
+ }
+ }
+
+ if (subpass.pDepthStencilAttachment && subpass.pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) {
+ uses_depthstencil_attachment = true;
+ }
+ }
+ graphicsPipelineCI.initialize(pCreateInfo, uses_color_attachment, uses_depthstencil_attachment);
// Make sure compute pipeline is null
VkComputePipelineCreateInfo emptyComputeCI = {};
computePipelineCI.initialize(&emptyComputeCI);
@@ -588,15 +601,15 @@
this->duplicate_shaders |= this->active_shaders & pPSSCI->stage;
this->active_shaders |= pPSSCI->stage;
}
- if (pCreateInfo->pVertexInputState) {
- const VkPipelineVertexInputStateCreateInfo *pVICI = pCreateInfo->pVertexInputState;
+ if (graphicsPipelineCI.pVertexInputState) {
+ const auto pVICI = graphicsPipelineCI.pVertexInputState;
if (pVICI->vertexBindingDescriptionCount) {
this->vertexBindingDescriptions = std::vector<VkVertexInputBindingDescription>(
pVICI->pVertexBindingDescriptions, pVICI->pVertexBindingDescriptions + pVICI->vertexBindingDescriptionCount);
}
}
- if (pCreateInfo->pColorBlendState) {
- const VkPipelineColorBlendStateCreateInfo *pCBCI = pCreateInfo->pColorBlendState;
+ if (graphicsPipelineCI.pColorBlendState) {
+ const auto pCBCI = graphicsPipelineCI.pColorBlendState;
if (pCBCI->attachmentCount) {
this->attachments = std::vector<VkPipelineColorBlendAttachmentState>(pCBCI->pAttachments,
pCBCI->pAttachments + pCBCI->attachmentCount);
@@ -609,7 +622,7 @@
computePipelineCI.initialize(pCreateInfo);
// Make sure gfx pipeline is null
VkGraphicsPipelineCreateInfo emptyGraphicsCI = {};
- graphicsPipelineCI.initialize(&emptyGraphicsCI);
+ graphicsPipelineCI.initialize(&emptyGraphicsCI, false, false);
switch (computePipelineCI.stage.stage) {
case VK_SHADER_STAGE_COMPUTE_BIT:
this->active_shaders |= VK_SHADER_STAGE_COMPUTE_BIT;
diff --git a/layers/parameter_validation.h b/layers/parameter_validation.h
index 8acd34b..ced0810 100644
--- a/layers/parameter_validation.h
+++ b/layers/parameter_validation.h
@@ -27,6 +27,7 @@
#include <sstream>
#include <bitset>
#include <mutex>
+#include <unordered_map>
#include <unordered_set>
#include "vulkan/vulkan.h"
@@ -81,6 +82,13 @@
VkDevice device = VK_NULL_HANDLE;
DeviceExtensions extensions;
+ struct SubpassesUsageStates {
+ std::unordered_set<uint32_t> subpasses_using_color_attachment;
+ std::unordered_set<uint32_t> subpasses_using_depthstencil_attachment;
+ };
+
+ std::unordered_map<VkRenderPass, SubpassesUsageStates> renderpasses_states;
+
VkLayerDispatchTable dispatch_table = {};
};
diff --git a/layers/parameter_validation_utils.cpp b/layers/parameter_validation_utils.cpp
index 7617826..3058cb4 100644
--- a/layers/parameter_validation_utils.cpp
+++ b/layers/parameter_validation_utils.cpp
@@ -83,6 +83,10 @@
const VkAllocationCallbacks *pAllocator);
extern bool parameter_validation_vkCreateCommandPool(VkDevice device, const VkCommandPoolCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkCommandPool *pCommandPool);
+extern bool parameter_validation_vkCreateRenderPass(VkDevice device, const VkRenderPassCreateInfo *pCreateInfo,
+ const VkAllocationCallbacks *pAllocator, VkRenderPass *pRenderPass);
+extern bool parameter_validation_vkDestroyRenderPass(VkDevice device, VkRenderPass renderPass,
+ const VkAllocationCallbacks *pAllocator);
// TODO : This can be much smarter, using separate locks for separate global data
std::mutex global_lock;
@@ -578,6 +582,76 @@
return result;
}
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateRenderPass(VkDevice device, const VkRenderPassCreateInfo *pCreateInfo,
+ const VkAllocationCallbacks *pAllocator, VkRenderPass *pRenderPass) {
+ layer_data *device_data = GetLayerDataPtr(get_dispatch_key(device), layer_data_map);
+ bool skip = false;
+ VkResult result = VK_ERROR_VALIDATION_FAILED_EXT;
+
+ {
+ std::unique_lock<std::mutex> lock(global_lock);
+ skip |= parameter_validation_vkCreateRenderPass(device, pCreateInfo, pAllocator, pRenderPass);
+
+ typedef bool (*PFN_manual_vkCreateRenderPass)(VkDevice device, const VkRenderPassCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
+ PFN_manual_vkCreateRenderPass custom_func = (PFN_manual_vkCreateRenderPass)custom_functions["vkCreateRenderPass"];
+ if (custom_func != nullptr) {
+ skip |= custom_func(device, pCreateInfo, pAllocator, pRenderPass);
+ }
+ }
+
+ if (!skip) {
+ result = device_data->dispatch_table.CreateRenderPass(device, pCreateInfo, pAllocator, pRenderPass);
+
+ // track the state necessary for checking vkCreateGraphicsPipeline (subpass usage of depth and color attachments)
+ if (result == VK_SUCCESS) {
+ std::unique_lock<std::mutex> lock(global_lock);
+ const auto renderPass = *pRenderPass;
+ auto &renderpass_state = device_data->renderpasses_states[renderPass];
+
+ for (uint32_t subpass = 0; subpass < pCreateInfo->subpassCount; ++subpass) {
+ bool uses_color = false;
+ for (uint32_t i = 0; i < pCreateInfo->pSubpasses[subpass].colorAttachmentCount && !uses_color; ++i)
+ if (pCreateInfo->pSubpasses[subpass].pColorAttachments[i].attachment != VK_ATTACHMENT_UNUSED) uses_color = true;
+
+ bool uses_depthstencil = false;
+ if (pCreateInfo->pSubpasses[subpass].pDepthStencilAttachment)
+ if (pCreateInfo->pSubpasses[subpass].pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED)
+ uses_depthstencil = true;
+
+ if (uses_color) renderpass_state.subpasses_using_color_attachment.insert(subpass);
+ if (uses_depthstencil) renderpass_state.subpasses_using_depthstencil_attachment.insert(subpass);
+ }
+ }
+ }
+ return result;
+}
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyRenderPass(VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks *pAllocator) {
+ layer_data *device_data = GetLayerDataPtr(get_dispatch_key(device), layer_data_map);
+ bool skip = false;
+
+ {
+ std::unique_lock<std::mutex> lock(global_lock);
+ skip |= parameter_validation_vkDestroyRenderPass(device, renderPass, pAllocator);
+
+ typedef bool (*PFN_manual_vkDestroyRenderPass)(VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks* pAllocator);
+ PFN_manual_vkDestroyRenderPass custom_func = (PFN_manual_vkDestroyRenderPass)custom_functions["vkDestroyRenderPass"];
+ if (custom_func != nullptr) {
+ skip |= custom_func(device, renderPass, pAllocator);
+ }
+ }
+
+ if (!skip) {
+ device_data->dispatch_table.DestroyRenderPass(device, renderPass, pAllocator);
+
+ // track the state necessary for checking vkCreateGraphicsPipeline (subpass usage of depth and color attachments)
+ {
+ std::unique_lock<std::mutex> lock(global_lock);
+ device_data->renderpasses_states.erase(renderPass);
+ }
+ }
+}
+
bool pv_vkCreateBuffer(VkDevice device, const VkBufferCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator,
VkBuffer *pBuffer) {
bool skip = false;
@@ -1238,8 +1312,20 @@
}
}
- // TODO: Conditional NULL check based on subpass depth/stencil attachment
- if (pCreateInfos[i].pDepthStencilState != nullptr) {
+ bool uses_color_attachment = false;
+ bool uses_depthstencil_attachment = false;
+ {
+ const auto subpasses_uses_it = device_data->renderpasses_states.find(pCreateInfos[i].renderPass);
+ if (subpasses_uses_it != device_data->renderpasses_states.end()) {
+ const auto &subpasses_uses = subpasses_uses_it->second;
+ if (subpasses_uses.subpasses_using_color_attachment.count(pCreateInfos[i].subpass))
+ uses_color_attachment = true;
+ if (subpasses_uses.subpasses_using_depthstencil_attachment.count(pCreateInfos[i].subpass))
+ uses_depthstencil_attachment = true;
+ }
+ }
+
+ if (pCreateInfos[i].pDepthStencilState != nullptr && uses_depthstencil_attachment) {
skip |= validate_struct_pnext(
report_data, "vkCreateGraphicsPipelines",
ParameterName("pCreateInfos[%i].pDepthStencilState->pNext", ParameterName::IndexVector{i}), NULL,
@@ -1333,8 +1419,7 @@
}
}
- // TODO: Conditional NULL check based on subpass color attachment
- if (pCreateInfos[i].pColorBlendState != nullptr) {
+ if (pCreateInfos[i].pColorBlendState != nullptr && uses_color_attachment) {
skip |= validate_struct_pnext(
report_data, "vkCreateGraphicsPipelines",
ParameterName("pCreateInfos[%i].pColorBlendState->pNext", ParameterName::IndexVector{i}), NULL,
diff --git a/layers/shader_validation.cpp b/layers/shader_validation.cpp
index 7847e3d..0af30fb 100644
--- a/layers/shader_validation.cpp
+++ b/layers/shader_validation.cpp
@@ -771,7 +771,7 @@
static bool validate_fs_outputs_against_render_pass(debug_report_data const *report_data, shader_module const *fs,
spirv_inst_iter entrypoint, PIPELINE_STATE const *pipeline,
uint32_t subpass_index) {
- auto rpci = pipeline->render_pass_ci.ptr();
+ auto rpci = pipeline->rp_state->createInfo.ptr();
std::map<uint32_t, VkFormat> color_attachments;
auto subpass = rpci->pSubpasses[subpass_index];
@@ -1356,7 +1356,7 @@
if (pStage->stage == VK_SHADER_STAGE_FRAGMENT_BIT) {
auto input_attachment_uses = collect_interface_by_input_attachment_index(module, accessible_ids);
- auto rpci = pipeline->render_pass_ci.ptr();
+ auto rpci = pipeline->rp_state->createInfo.ptr();
auto subpass = pipeline->graphicsPipelineCI.subpass;
for (auto use : input_attachment_uses) {
diff --git a/layers/unique_objects.cpp b/layers/unique_objects.cpp
index 8515a64..0fa94a7 100644
--- a/layers/unique_objects.cpp
+++ b/layers/unique_objects.cpp
@@ -328,7 +328,22 @@
local_pCreateInfos = new safe_VkGraphicsPipelineCreateInfo[createInfoCount];
std::lock_guard<std::mutex> lock(global_lock);
for (uint32_t idx0 = 0; idx0 < createInfoCount; ++idx0) {
- local_pCreateInfos[idx0].initialize(&pCreateInfos[idx0]);
+ bool uses_color_attachment = false;
+ bool uses_depthstencil_attachment = false;
+ {
+ const auto subpasses_uses_it =
+ device_data->renderpasses_states.find(Unwrap(device_data, pCreateInfos[idx0].renderPass));
+ if (subpasses_uses_it != device_data->renderpasses_states.end()) {
+ const auto &subpasses_uses = subpasses_uses_it->second;
+ if (subpasses_uses.subpasses_using_color_attachment.count(pCreateInfos[idx0].subpass))
+ uses_color_attachment = true;
+ if (subpasses_uses.subpasses_using_depthstencil_attachment.count(pCreateInfos[idx0].subpass))
+ uses_depthstencil_attachment = true;
+ }
+ }
+
+ local_pCreateInfos[idx0].initialize(&pCreateInfos[idx0], uses_color_attachment, uses_depthstencil_attachment);
+
if (pCreateInfos[idx0].basePipelineHandle) {
local_pCreateInfos[idx0].basePipelineHandle = Unwrap(device_data, pCreateInfos[idx0].basePipelineHandle);
}
@@ -366,6 +381,55 @@
return result;
}
+static void PostCallCreateRenderPass(layer_data *dev_data, const VkRenderPassCreateInfo *pCreateInfo, VkRenderPass renderPass) {
+ auto &renderpass_state = dev_data->renderpasses_states[renderPass];
+
+ for (uint32_t subpass = 0; subpass < pCreateInfo->subpassCount; ++subpass) {
+ bool uses_color = false;
+ for (uint32_t i = 0; i < pCreateInfo->pSubpasses[subpass].colorAttachmentCount && !uses_color; ++i)
+ if (pCreateInfo->pSubpasses[subpass].pColorAttachments[i].attachment != VK_ATTACHMENT_UNUSED) uses_color = true;
+
+ bool uses_depthstencil = false;
+ if (pCreateInfo->pSubpasses[subpass].pDepthStencilAttachment)
+ if (pCreateInfo->pSubpasses[subpass].pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED)
+ uses_depthstencil = true;
+
+ if (uses_color) renderpass_state.subpasses_using_color_attachment.insert(subpass);
+ if (uses_depthstencil) renderpass_state.subpasses_using_depthstencil_attachment.insert(subpass);
+ }
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL CreateRenderPass(VkDevice device, const VkRenderPassCreateInfo *pCreateInfo,
+ const VkAllocationCallbacks *pAllocator, VkRenderPass *pRenderPass) {
+ layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(device), layer_data_map);
+ VkResult result = dev_data->dispatch_table.CreateRenderPass(device, pCreateInfo, pAllocator, pRenderPass);
+ if (VK_SUCCESS == result) {
+ std::lock_guard<std::mutex> lock(global_lock);
+
+ PostCallCreateRenderPass(dev_data, pCreateInfo, *pRenderPass);
+
+ *pRenderPass = WrapNew(dev_data, *pRenderPass);
+ }
+ return result;
+}
+
+static void PostCallDestroyRenderPass(layer_data *dev_data, VkRenderPass renderPass) {
+ dev_data->renderpasses_states.erase(renderPass);
+}
+
+VKAPI_ATTR void VKAPI_CALL DestroyRenderPass(VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks *pAllocator) {
+ layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(device), layer_data_map);
+ std::unique_lock<std::mutex> lock(global_lock);
+ uint64_t renderPass_id = reinterpret_cast<uint64_t &>(renderPass);
+ renderPass = (VkRenderPass)dev_data->unique_id_mapping[renderPass_id];
+ dev_data->unique_id_mapping.erase(renderPass_id);
+ lock.unlock();
+ dev_data->dispatch_table.DestroyRenderPass(device, renderPass, pAllocator);
+
+ lock.lock();
+ PostCallDestroyRenderPass(dev_data, renderPass);
+}
+
VKAPI_ATTR VkResult VKAPI_CALL CreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapchain) {
layer_data *my_map_data = GetLayerDataPtr(get_dispatch_key(device), layer_data_map);
diff --git a/layers/unique_objects.h b/layers/unique_objects.h
index 57b9dbf..e604e91 100644
--- a/layers/unique_objects.h
+++ b/layers/unique_objects.h
@@ -21,6 +21,9 @@
#include "vulkan/vulkan.h"
+#include <unordered_map>
+#include <unordered_set>
+
#include "vk_layer_data.h"
#include "vk_safe_struct.h"
#include "vk_layer_utils.h"
@@ -69,6 +72,13 @@
std::unordered_map<uint64_t, uint64_t> unique_id_mapping; // Map uniqueID to actual object handle
VkPhysicalDevice gpu;
+ struct SubpassesUsageStates {
+ std::unordered_set<uint32_t> subpasses_using_color_attachment;
+ std::unordered_set<uint32_t> subpasses_using_depthstencil_attachment;
+ };
+ // uses unwrapped handles
+ std::unordered_map<VkRenderPass, SubpassesUsageStates> renderpasses_states;
+
layer_data() : wsi_enabled(false), gpu(VK_NULL_HANDLE){};
};
diff --git a/scripts/helper_file_generator.py b/scripts/helper_file_generator.py
index 4b1ddc6..1e59d0a 100644
--- a/scripts/helper_file_generator.py
+++ b/scripts/helper_file_generator.py
@@ -93,6 +93,15 @@
self.StructType = namedtuple('StructType', ['name', 'value'])
self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isconst', 'iscount', 'len', 'extstructs', 'cdecl'])
self.StructMemberData = namedtuple('StructMemberData', ['name', 'members', 'ifdef_protect'])
+
+ self.custom_construct_params = {
+ # safe_VkGraphicsPipelineCreateInfo needs to know if subpass has color and\or depth\stencil attachments to use its pointers
+ 'VkGraphicsPipelineCreateInfo' :
+ ', const bool uses_color_attachment, const bool uses_depthstencil_attachment',
+ # safe_VkPipelineViewportStateCreateInfo needs to know if viewport and scissor is dynamic to use its pointers
+ 'VkPipelineViewportStateCreateInfo' :
+ ', const bool is_dynamic_viewports, const bool is_dynamic_scissors',
+ }
#
# Called once at the beginning of each run
def beginFile(self, genOpts):
@@ -580,13 +589,13 @@
safe_struct_header += ' %s* %s;\n' % (member.type, member.name)
else:
safe_struct_header += '%s;\n' % member.cdecl
- safe_struct_header += ' safe_%s(const %s* in_struct);\n' % (item.name, item.name)
+ safe_struct_header += ' safe_%s(const %s* in_struct%s);\n' % (item.name, item.name, self.custom_construct_params.get(item.name, ''))
safe_struct_header += ' safe_%s(const safe_%s& src);\n' % (item.name, item.name)
safe_struct_header += ' safe_%s& operator=(const safe_%s& src);\n' % (item.name, item.name)
safe_struct_header += ' safe_%s();\n' % item.name
safe_struct_header += ' ~safe_%s();\n' % item.name
- safe_struct_header += ' void initialize(const %s* in_struct);\n' % item.name
- safe_struct_header += ' void initialize(const safe_%s* src);\n' % item.name
+ safe_struct_header += ' void initialize(const %s* in_struct%s);\n' % (item.name, self.custom_construct_params.get(item.name, ''))
+ safe_struct_header += ' void initialize(const safe_%s* src);\n' % (item.name)
safe_struct_header += ' %s *ptr() { return reinterpret_cast<%s *>(this); }\n' % (item.name, item.name)
safe_struct_header += ' %s const *ptr() const { return reinterpret_cast<%s const *>(this); }\n' % (item.name, item.name)
safe_struct_header += '};\n'
@@ -768,49 +777,196 @@
init_func_txt = '' # Txt for initialize() function that takes struct ptr and inits members
construct_txt = '' # Body of constuctor as well as body of initialize() func following init_func_txt
destruct_txt = ''
- # VkWriteDescriptorSet is special case because pointers may be non-null but ignored
- custom_construct_txt = {'VkWriteDescriptorSet' :
- ' switch (descriptorType) {\n'
- ' case VK_DESCRIPTOR_TYPE_SAMPLER:\n'
- ' case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:\n'
- ' case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:\n'
- ' case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:\n'
- ' case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:\n'
- ' if (descriptorCount && in_struct->pImageInfo) {\n'
- ' pImageInfo = new VkDescriptorImageInfo[descriptorCount];\n'
- ' for (uint32_t i=0; i<descriptorCount; ++i) {\n'
- ' pImageInfo[i] = in_struct->pImageInfo[i];\n'
- ' }\n'
- ' }\n'
- ' break;\n'
- ' case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:\n'
- ' case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:\n'
- ' case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:\n'
- ' case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:\n'
- ' if (descriptorCount && in_struct->pBufferInfo) {\n'
- ' pBufferInfo = new VkDescriptorBufferInfo[descriptorCount];\n'
- ' for (uint32_t i=0; i<descriptorCount; ++i) {\n'
- ' pBufferInfo[i] = in_struct->pBufferInfo[i];\n'
- ' }\n'
- ' }\n'
- ' break;\n'
- ' case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:\n'
- ' case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:\n'
- ' if (descriptorCount && in_struct->pTexelBufferView) {\n'
- ' pTexelBufferView = new VkBufferView[descriptorCount];\n'
- ' for (uint32_t i=0; i<descriptorCount; ++i) {\n'
- ' pTexelBufferView[i] = in_struct->pTexelBufferView[i];\n'
- ' }\n'
- ' }\n'
- ' break;\n'
- ' default:\n'
- ' break;\n'
- ' }\n',
- 'VkShaderModuleCreateInfo' :
- ' if (in_struct->pCode) {\n'
- ' pCode = reinterpret_cast<uint32_t *>(new uint8_t[codeSize]);\n'
- ' memcpy((void *)pCode, (void *)in_struct->pCode, codeSize);\n'
- ' }\n'}
+
+ custom_construct_txt = {
+ # VkWriteDescriptorSet is special case because pointers may be non-null but ignored
+ 'VkWriteDescriptorSet' :
+ ' switch (descriptorType) {\n'
+ ' case VK_DESCRIPTOR_TYPE_SAMPLER:\n'
+ ' case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:\n'
+ ' case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:\n'
+ ' case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:\n'
+ ' case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:\n'
+ ' if (descriptorCount && in_struct->pImageInfo) {\n'
+ ' pImageInfo = new VkDescriptorImageInfo[descriptorCount];\n'
+ ' for (uint32_t i=0; i<descriptorCount; ++i) {\n'
+ ' pImageInfo[i] = in_struct->pImageInfo[i];\n'
+ ' }\n'
+ ' }\n'
+ ' break;\n'
+ ' case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:\n'
+ ' case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:\n'
+ ' case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:\n'
+ ' case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:\n'
+ ' if (descriptorCount && in_struct->pBufferInfo) {\n'
+ ' pBufferInfo = new VkDescriptorBufferInfo[descriptorCount];\n'
+ ' for (uint32_t i=0; i<descriptorCount; ++i) {\n'
+ ' pBufferInfo[i] = in_struct->pBufferInfo[i];\n'
+ ' }\n'
+ ' }\n'
+ ' break;\n'
+ ' case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:\n'
+ ' case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:\n'
+ ' if (descriptorCount && in_struct->pTexelBufferView) {\n'
+ ' pTexelBufferView = new VkBufferView[descriptorCount];\n'
+ ' for (uint32_t i=0; i<descriptorCount; ++i) {\n'
+ ' pTexelBufferView[i] = in_struct->pTexelBufferView[i];\n'
+ ' }\n'
+ ' }\n'
+ ' break;\n'
+ ' default:\n'
+ ' break;\n'
+ ' }\n',
+ 'VkShaderModuleCreateInfo' :
+ ' if (in_struct->pCode) {\n'
+ ' pCode = reinterpret_cast<uint32_t *>(new uint8_t[codeSize]);\n'
+ ' memcpy((void *)pCode, (void *)in_struct->pCode, codeSize);\n'
+ ' }\n',
+ # VkGraphicsPipelineCreateInfo is special case because its pointers may be non-null but ignored
+ 'VkGraphicsPipelineCreateInfo' :
+ ' if (stageCount && in_struct->pStages) {\n'
+ ' pStages = new safe_VkPipelineShaderStageCreateInfo[stageCount];\n'
+ ' for (uint32_t i=0; i<stageCount; ++i) {\n'
+ ' pStages[i].initialize(&in_struct->pStages[i]);\n'
+ ' }\n'
+ ' }\n'
+ ' if (in_struct->pVertexInputState)\n'
+ ' pVertexInputState = new safe_VkPipelineVertexInputStateCreateInfo(in_struct->pVertexInputState);\n'
+ ' else\n'
+ ' pVertexInputState = NULL;\n'
+ ' if (in_struct->pInputAssemblyState)\n'
+ ' pInputAssemblyState = new safe_VkPipelineInputAssemblyStateCreateInfo(in_struct->pInputAssemblyState);\n'
+ ' else\n'
+ ' pInputAssemblyState = NULL;\n'
+ ' bool has_tessellation_stage = false;\n'
+ ' if (stageCount && pStages)\n'
+ ' for (uint32_t i=0; i<stageCount && !has_tessellation_stage; ++i)\n'
+ ' if (pStages[i].stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT || pStages[i].stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)\n'
+ ' has_tessellation_stage = true;\n'
+ ' if (in_struct->pTessellationState && has_tessellation_stage)\n'
+ ' pTessellationState = new safe_VkPipelineTessellationStateCreateInfo(in_struct->pTessellationState);\n'
+ ' else\n'
+ ' pTessellationState = NULL; // original pTessellationState pointer ignored\n'
+ ' bool has_rasterization = in_struct->pRasterizationState ? !in_struct->pRasterizationState->rasterizerDiscardEnable : false;\n'
+ ' if (in_struct->pViewportState && has_rasterization) {\n'
+ ' bool is_dynamic_viewports = false;\n'
+ ' bool is_dynamic_scissors = false;\n'
+ ' if (in_struct->pDynamicState && in_struct->pDynamicState->pDynamicStates) {\n'
+ ' for (uint32_t i = 0; i < in_struct->pDynamicState->dynamicStateCount && !is_dynamic_viewports; ++i)\n'
+ ' if (in_struct->pDynamicState->pDynamicStates[i] == VK_DYNAMIC_STATE_VIEWPORT)\n'
+ ' is_dynamic_viewports = true;\n'
+ ' for (uint32_t i = 0; i < in_struct->pDynamicState->dynamicStateCount && !is_dynamic_scissors; ++i)\n'
+ ' if (in_struct->pDynamicState->pDynamicStates[i] == VK_DYNAMIC_STATE_SCISSOR)\n'
+ ' is_dynamic_scissors = true;\n'
+ ' }\n'
+ ' pViewportState = new safe_VkPipelineViewportStateCreateInfo(in_struct->pViewportState, is_dynamic_viewports, is_dynamic_scissors);\n'
+ ' } else\n'
+ ' pViewportState = NULL; // original pViewportState pointer ignored\n'
+ ' if (in_struct->pRasterizationState)\n'
+ ' pRasterizationState = new safe_VkPipelineRasterizationStateCreateInfo(in_struct->pRasterizationState);\n'
+ ' else\n'
+ ' pRasterizationState = NULL;\n'
+ ' if (in_struct->pMultisampleState && has_rasterization)\n'
+ ' pMultisampleState = new safe_VkPipelineMultisampleStateCreateInfo(in_struct->pMultisampleState);\n'
+ ' else\n'
+ ' pMultisampleState = NULL; // original pMultisampleState pointer ignored\n'
+ ' // needs a tracked subpass state uses_depthstencil_attachment\n'
+ ' if (in_struct->pDepthStencilState && has_rasterization && uses_depthstencil_attachment)\n'
+ ' pDepthStencilState = new safe_VkPipelineDepthStencilStateCreateInfo(in_struct->pDepthStencilState);\n'
+ ' else\n'
+ ' pDepthStencilState = NULL; // original pDepthStencilState pointer ignored\n'
+ ' // needs a tracked subpass state usesColorAttachment\n'
+ ' if (in_struct->pColorBlendState && has_rasterization && uses_color_attachment)\n'
+ ' pColorBlendState = new safe_VkPipelineColorBlendStateCreateInfo(in_struct->pColorBlendState);\n'
+ ' else\n'
+ ' pColorBlendState = NULL; // original pColorBlendState pointer ignored\n'
+ ' if (in_struct->pDynamicState)\n'
+ ' pDynamicState = new safe_VkPipelineDynamicStateCreateInfo(in_struct->pDynamicState);\n'
+ ' else\n'
+ ' pDynamicState = NULL;\n',
+ # VkPipelineViewportStateCreateInfo is special case because its pointers may be non-null but ignored
+ 'VkPipelineViewportStateCreateInfo' :
+ ' if (in_struct->pViewports && !is_dynamic_viewports) {\n'
+ ' pViewports = new VkViewport[in_struct->viewportCount];\n'
+ ' memcpy ((void *)pViewports, (void *)in_struct->pViewports, sizeof(VkViewport)*in_struct->viewportCount);\n'
+ ' }\n'
+ ' else\n'
+ ' pViewports = NULL;\n'
+ ' if (in_struct->pScissors && !is_dynamic_scissors) {\n'
+ ' pScissors = new VkRect2D[in_struct->scissorCount];\n'
+ ' memcpy ((void *)pScissors, (void *)in_struct->pScissors, sizeof(VkRect2D)*in_struct->scissorCount);\n'
+ ' }\n'
+ ' else\n'
+ ' pScissors = NULL;\n',
+ }
+
+ custom_copy_txt = {
+ # VkGraphicsPipelineCreateInfo is special case because it has custom construct parameters
+ 'VkGraphicsPipelineCreateInfo' :
+ ' if (stageCount && src.pStages) {\n'
+ ' pStages = new safe_VkPipelineShaderStageCreateInfo[stageCount];\n'
+ ' for (uint32_t i=0; i<stageCount; ++i) {\n'
+ ' pStages[i].initialize(&src.pStages[i]);\n'
+ ' }\n'
+ ' }\n'
+ ' if (src.pVertexInputState)\n'
+ ' pVertexInputState = new safe_VkPipelineVertexInputStateCreateInfo(*src.pVertexInputState);\n'
+ ' else\n'
+ ' pVertexInputState = NULL;\n'
+ ' if (src.pInputAssemblyState)\n'
+ ' pInputAssemblyState = new safe_VkPipelineInputAssemblyStateCreateInfo(*src.pInputAssemblyState);\n'
+ ' else\n'
+ ' pInputAssemblyState = NULL;\n'
+ ' bool has_tessellation_stage = false;\n'
+ ' if (stageCount && pStages)\n'
+ ' for (uint32_t i=0; i<stageCount && !has_tessellation_stage; ++i)\n'
+ ' if (pStages[i].stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT || pStages[i].stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)\n'
+ ' has_tessellation_stage = true;\n'
+ ' if (src.pTessellationState && has_tessellation_stage)\n'
+ ' pTessellationState = new safe_VkPipelineTessellationStateCreateInfo(*src.pTessellationState);\n'
+ ' else\n'
+ ' pTessellationState = NULL; // original pTessellationState pointer ignored\n'
+ ' bool has_rasterization = src.pRasterizationState ? !src.pRasterizationState->rasterizerDiscardEnable : false;\n'
+ ' if (src.pViewportState && has_rasterization) {\n'
+ ' pViewportState = new safe_VkPipelineViewportStateCreateInfo(*src.pViewportState);\n'
+ ' } else\n'
+ ' pViewportState = NULL; // original pViewportState pointer ignored\n'
+ ' if (src.pRasterizationState)\n'
+ ' pRasterizationState = new safe_VkPipelineRasterizationStateCreateInfo(*src.pRasterizationState);\n'
+ ' else\n'
+ ' pRasterizationState = NULL;\n'
+ ' if (src.pMultisampleState && has_rasterization)\n'
+ ' pMultisampleState = new safe_VkPipelineMultisampleStateCreateInfo(*src.pMultisampleState);\n'
+ ' else\n'
+ ' pMultisampleState = NULL; // original pMultisampleState pointer ignored\n'
+ ' if (src.pDepthStencilState && has_rasterization)\n'
+ ' pDepthStencilState = new safe_VkPipelineDepthStencilStateCreateInfo(*src.pDepthStencilState);\n'
+ ' else\n'
+ ' pDepthStencilState = NULL; // original pDepthStencilState pointer ignored\n'
+ ' if (src.pColorBlendState && has_rasterization)\n'
+ ' pColorBlendState = new safe_VkPipelineColorBlendStateCreateInfo(*src.pColorBlendState);\n'
+ ' else\n'
+ ' pColorBlendState = NULL; // original pColorBlendState pointer ignored\n'
+ ' if (src.pDynamicState)\n'
+ ' pDynamicState = new safe_VkPipelineDynamicStateCreateInfo(*src.pDynamicState);\n'
+ ' else\n'
+ ' pDynamicState = NULL;\n',
+ # VkPipelineViewportStateCreateInfo is special case because it has custom construct parameters
+ 'VkPipelineViewportStateCreateInfo' :
+ ' if (src.pViewports) {\n'
+ ' pViewports = new VkViewport[src.viewportCount];\n'
+ ' memcpy ((void *)pViewports, (void *)src.pViewports, sizeof(VkViewport)*src.viewportCount);\n'
+ ' }\n'
+ ' else\n'
+ ' pViewports = NULL;\n'
+ ' if (src.pScissors) {\n'
+ ' pScissors = new VkRect2D[src.scissorCount];\n'
+ ' memcpy ((void *)pScissors, (void *)src.pScissors, sizeof(VkRect2D)*src.scissorCount);\n'
+ ' }\n'
+ ' else\n'
+ ' pScissors = NULL;\n',
+ }
+
custom_destruct_txt = {'VkShaderModuleCreateInfo' :
' if (pCode)\n'
' delete[] reinterpret_cast<const uint8_t *>(pCode);\n' }
@@ -892,7 +1048,7 @@
construct_txt = custom_construct_txt[item.name]
if item.name in custom_destruct_txt:
destruct_txt = custom_destruct_txt[item.name]
- safe_struct_body.append("\n%s::%s(const %s* in_struct) :%s\n{\n%s}" % (ss_name, ss_name, item.name, init_list, construct_txt))
+ safe_struct_body.append("\n%s::%s(const %s* in_struct%s) :%s\n{\n%s}" % (ss_name, ss_name, item.name, self.custom_construct_params.get(item.name, ''), init_list, construct_txt))
if '' != default_init_list:
default_init_list = " :%s" % (default_init_list[:-1])
safe_struct_body.append("\n%s::%s()%s\n{}" % (ss_name, ss_name, default_init_list))
@@ -901,11 +1057,13 @@
copy_construct_txt = construct_txt.replace(' (in_struct->', ' (src.') # Exclude 'if' blocks from next line
copy_construct_txt = copy_construct_txt.replace('(in_struct->', '(*src.') # Pass object to copy constructors
copy_construct_txt = copy_construct_txt.replace('in_struct->', 'src.') # Modify remaining struct refs for src object
+ if item.name in custom_copy_txt:
+ copy_construct_txt = custom_copy_txt[item.name]
copy_assign_txt = ' if (&src == this) return *this;\n\n' + destruct_txt + '\n' + copy_construct_init + copy_construct_txt + '\n return *this;'
safe_struct_body.append("\n%s::%s(const %s& src)\n{\n%s%s}" % (ss_name, ss_name, ss_name, copy_construct_init, copy_construct_txt)) # Copy constructor
safe_struct_body.append("\n%s& %s::operator=(const %s& src)\n{\n%s\n}" % (ss_name, ss_name, ss_name, copy_assign_txt)) # Copy assignment operator
safe_struct_body.append("\n%s::~%s()\n{\n%s}" % (ss_name, ss_name, destruct_txt))
- safe_struct_body.append("\nvoid %s::initialize(const %s* in_struct)\n{\n%s%s}" % (ss_name, item.name, init_func_txt, construct_txt))
+ safe_struct_body.append("\nvoid %s::initialize(const %s* in_struct%s)\n{\n%s%s}" % (ss_name, item.name, self.custom_construct_params.get(item.name, ''), init_func_txt, construct_txt))
# Copy initializer uses same txt as copy constructor but has a ptr and not a reference
init_copy = copy_construct_init.replace('src.', 'src->')
init_construct = copy_construct_txt.replace('src.', 'src->')
diff --git a/scripts/parameter_validation_generator.py b/scripts/parameter_validation_generator.py
index 9c673fc..ba825e2 100644
--- a/scripts/parameter_validation_generator.py
+++ b/scripts/parameter_validation_generator.py
@@ -154,6 +154,8 @@
'vkCreateDebugReportCallbackEXT',
'vkDestroyDebugReportCallbackEXT',
'vkCreateCommandPool',
+ 'vkCreateRenderPass',
+ 'vkDestroyRenderPass',
]
# Structure fields to ignore
self.structMemberBlacklist = { 'VkWriteDescriptorSet' : ['dstSet'] }
diff --git a/scripts/unique_objects_generator.py b/scripts/unique_objects_generator.py
index 9cfa954..c37bf0d 100644
--- a/scripts/unique_objects_generator.py
+++ b/scripts/unique_objects_generator.py
@@ -150,6 +150,8 @@
'vkGetPhysicalDeviceDisplayProperties2KHR',
'vkGetPhysicalDeviceDisplayPlaneProperties2KHR',
'vkGetDisplayModeProperties2KHR',
+ 'vkCreateRenderPass',
+ 'vkDestroyRenderPass',
]
# Commands shadowed by interface functions and are not implemented
self.interface_functions = [
diff --git a/tests/layer_validation_tests.cpp b/tests/layer_validation_tests.cpp
index d302036..844d12a 100644
--- a/tests/layer_validation_tests.cpp
+++ b/tests/layer_validation_tests.cpp
@@ -25755,6 +25755,269 @@
}
}
+TEST_F(VkPositiveLayerTest, CreateGraphicsPipelineWithIgnoredPointers) {
+ TEST_DESCRIPTION("Create Graphics Pipeline with pointers that must be ignored by layers");
+
+ ASSERT_NO_FATAL_FAILURE(Init());
+
+ m_depth_stencil_fmt = FindSupportedDepthStencilFormat(gpu());
+ ASSERT_TRUE(m_depth_stencil_fmt != 0);
+
+ m_depthStencil->Init(m_device, static_cast<int32_t>(m_width), static_cast<int32_t>(m_height), m_depth_stencil_fmt);
+
+ ASSERT_NO_FATAL_FAILURE(InitRenderTarget(m_depthStencil->BindInfo()));
+
+ const uint64_t fake_address_64 = 0xCDCDCDCDCDCDCDCD;
+ const uint64_t fake_address_32 = 0xCDCDCDCD;
+ void *hopefully_undereferencable_pointer =
+ sizeof(void *) == 8 ? reinterpret_cast<void *>(fake_address_64) : reinterpret_cast<void *>(fake_address_32);
+
+ VkShaderObj vs(m_device, "#version 450\nvoid main(){gl_Position = vec4(0.0, 0.0, 0.0, 1.0);}\n", VK_SHADER_STAGE_VERTEX_BIT,
+ this);
+
+ const VkPipelineVertexInputStateCreateInfo pipeline_vertex_input_state_create_info{
+ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+ nullptr, // pNext
+ 0, // flags
+ 0,
+ nullptr, // bindings
+ 0,
+ nullptr // attributes
+ };
+
+ const VkPipelineInputAssemblyStateCreateInfo pipeline_input_assembly_state_create_info{
+ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
+ nullptr, // pNext
+ 0, // flags
+ VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
+ VK_FALSE // primitive restart
+ };
+
+ const VkPipelineRasterizationStateCreateInfo pipeline_rasterization_state_create_info_template{
+ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
+ nullptr, // pNext
+ 0, // flags
+ VK_FALSE, // depthClamp
+ VK_FALSE, // rasterizerDiscardEnable
+ VK_POLYGON_MODE_FILL,
+ VK_CULL_MODE_NONE,
+ VK_FRONT_FACE_COUNTER_CLOCKWISE,
+ VK_FALSE, // depthBias
+ 0.0f,
+ 0.0f,
+ 0.0f, // depthBias params
+ 1.0f // lineWidth
+ };
+
+ VkPipelineLayout pipeline_layout;
+ {
+ VkPipelineLayoutCreateInfo pipeline_layout_create_info{
+ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+ nullptr, // pNext
+ 0, // flags
+ 0,
+ nullptr, // layouts
+ 0,
+ nullptr // push constants
+ };
+
+ VkResult err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_create_info, nullptr, &pipeline_layout);
+ ASSERT_VK_SUCCESS(err);
+ }
+
+ // try disabled rasterizer and no tessellation
+ {
+ m_errorMonitor->ExpectSuccess();
+
+ VkPipelineRasterizationStateCreateInfo pipeline_rasterization_state_create_info =
+ pipeline_rasterization_state_create_info_template;
+ pipeline_rasterization_state_create_info.rasterizerDiscardEnable = VK_TRUE;
+
+ VkGraphicsPipelineCreateInfo graphics_pipeline_create_info{
+ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+ nullptr, // pNext
+ 0, // flags
+ 1, // stageCount
+ &vs.GetStageCreateInfo(),
+ &pipeline_vertex_input_state_create_info,
+ &pipeline_input_assembly_state_create_info,
+ reinterpret_cast<const VkPipelineTessellationStateCreateInfo *>(hopefully_undereferencable_pointer),
+ reinterpret_cast<const VkPipelineViewportStateCreateInfo *>(hopefully_undereferencable_pointer),
+ &pipeline_rasterization_state_create_info,
+ reinterpret_cast<const VkPipelineMultisampleStateCreateInfo *>(hopefully_undereferencable_pointer),
+ reinterpret_cast<const VkPipelineDepthStencilStateCreateInfo *>(hopefully_undereferencable_pointer),
+ reinterpret_cast<const VkPipelineColorBlendStateCreateInfo *>(hopefully_undereferencable_pointer),
+ nullptr, // dynamic states
+ pipeline_layout,
+ m_renderPass,
+ 0, // subpass
+ VK_NULL_HANDLE,
+ 0};
+
+ VkPipeline pipeline;
+ vkCreateGraphicsPipelines(m_device->handle(), VK_NULL_HANDLE, 1, &graphics_pipeline_create_info, nullptr, &pipeline);
+
+ m_errorMonitor->VerifyNotFound();
+
+ vkDestroyPipeline(m_device->handle(), pipeline, nullptr);
+ }
+
+ const VkPipelineMultisampleStateCreateInfo pipeline_multisample_state_create_info{
+ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
+ nullptr, // pNext
+ 0, // flags
+ VK_SAMPLE_COUNT_1_BIT,
+ VK_FALSE, // sample shading
+ 0.0f, // minSampleShading
+ nullptr, // pSampleMask
+ VK_FALSE, // alphaToCoverageEnable
+ VK_FALSE // alphaToOneEnable
+ };
+
+ // try enabled rasterizer but no subpass attachments
+ {
+ m_errorMonitor->ExpectSuccess();
+
+ VkPipelineRasterizationStateCreateInfo pipeline_rasterization_state_create_info =
+ pipeline_rasterization_state_create_info_template;
+ pipeline_rasterization_state_create_info.rasterizerDiscardEnable = VK_FALSE;
+
+ VkViewport viewport = {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
+ VkRect2D scissor = {{0, 0}, {static_cast<uint32_t>(m_width), static_cast<uint32_t>(m_height)}};
+
+ const VkPipelineViewportStateCreateInfo pipeline_viewport_state_create_info{
+ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
+ nullptr, // pNext
+ 0, // flags
+ 1,
+ &viewport,
+ 1,
+ &scissor};
+
+ VkRenderPass render_pass;
+ {
+ VkSubpassDescription subpass_desc = {};
+
+ VkRenderPassCreateInfo render_pass_create_info{
+ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
+ nullptr, // pNext
+ 0, // flags
+ 0,
+ nullptr, // attachments
+ 1,
+ &subpass_desc,
+ 0,
+ nullptr // subpass dependencies
+ };
+
+ VkResult err = vkCreateRenderPass(m_device->handle(), &render_pass_create_info, nullptr, &render_pass);
+ ASSERT_VK_SUCCESS(err);
+ }
+
+ VkGraphicsPipelineCreateInfo graphics_pipeline_create_info{
+ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+ nullptr, // pNext
+ 0, // flags
+ 1, // stageCount
+ &vs.GetStageCreateInfo(),
+ &pipeline_vertex_input_state_create_info,
+ &pipeline_input_assembly_state_create_info,
+ nullptr,
+ &pipeline_viewport_state_create_info,
+ &pipeline_rasterization_state_create_info,
+ &pipeline_multisample_state_create_info,
+ reinterpret_cast<const VkPipelineDepthStencilStateCreateInfo *>(hopefully_undereferencable_pointer),
+ reinterpret_cast<const VkPipelineColorBlendStateCreateInfo *>(hopefully_undereferencable_pointer),
+ nullptr, // dynamic states
+ pipeline_layout,
+ render_pass,
+ 0, // subpass
+ VK_NULL_HANDLE,
+ 0};
+
+ VkPipeline pipeline;
+ vkCreateGraphicsPipelines(m_device->handle(), VK_NULL_HANDLE, 1, &graphics_pipeline_create_info, nullptr, &pipeline);
+
+ m_errorMonitor->VerifyNotFound();
+
+ vkDestroyPipeline(m_device->handle(), pipeline, nullptr);
+ vkDestroyRenderPass(m_device->handle(), render_pass, nullptr);
+ }
+
+ // try dynamic viewport and scissor
+ {
+ m_errorMonitor->ExpectSuccess();
+
+ VkPipelineRasterizationStateCreateInfo pipeline_rasterization_state_create_info =
+ pipeline_rasterization_state_create_info_template;
+ pipeline_rasterization_state_create_info.rasterizerDiscardEnable = VK_FALSE;
+
+ const VkPipelineViewportStateCreateInfo pipeline_viewport_state_create_info{
+ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
+ nullptr, // pNext
+ 0, // flags
+ 1,
+ reinterpret_cast<const VkViewport *>(hopefully_undereferencable_pointer),
+ 1,
+ reinterpret_cast<const VkRect2D *>(hopefully_undereferencable_pointer)};
+
+ const VkPipelineDepthStencilStateCreateInfo pipeline_depth_stencil_state_create_info{
+ VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
+ nullptr, // pNext
+ 0, // flags
+ };
+
+ const VkPipelineColorBlendAttachmentState pipeline_color_blend_attachment_state = {};
+
+ const VkPipelineColorBlendStateCreateInfo pipeline_color_blend_state_create_info{
+ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
+ nullptr, // pNext
+ 0, // flags
+ VK_FALSE,
+ VK_LOGIC_OP_CLEAR,
+ 1,
+ &pipeline_color_blend_attachment_state,
+ {0.0f, 0.0f, 0.0f, 0.0f}};
+
+ const VkDynamicState dynamic_states[2] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
+
+ const VkPipelineDynamicStateCreateInfo pipeline_dynamic_state_create_info{
+ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
+ nullptr, // pNext
+ 0, // flags
+ 2, dynamic_states};
+
+ VkGraphicsPipelineCreateInfo graphics_pipeline_create_info{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+ nullptr, // pNext
+ 0, // flags
+ 1, // stageCount
+ &vs.GetStageCreateInfo(),
+ &pipeline_vertex_input_state_create_info,
+ &pipeline_input_assembly_state_create_info,
+ nullptr,
+ &pipeline_viewport_state_create_info,
+ &pipeline_rasterization_state_create_info,
+ &pipeline_multisample_state_create_info,
+ &pipeline_depth_stencil_state_create_info,
+ &pipeline_color_blend_state_create_info,
+ &pipeline_dynamic_state_create_info, // dynamic states
+ pipeline_layout,
+ m_renderPass,
+ 0, // subpass
+ VK_NULL_HANDLE,
+ 0};
+
+ VkPipeline pipeline;
+ vkCreateGraphicsPipelines(m_device->handle(), VK_NULL_HANDLE, 1, &graphics_pipeline_create_info, nullptr, &pipeline);
+
+ m_errorMonitor->VerifyNotFound();
+
+ vkDestroyPipeline(m_device->handle(), pipeline, nullptr);
+ }
+
+ vkDestroyPipelineLayout(m_device->handle(), pipeline_layout, nullptr);
+}
+
TEST_F(VkPositiveLayerTest, ExternalMemory) {
TEST_DESCRIPTION("Perform a copy through a pair of buffers linked by external memory");