diff --git a/layers/core_checks/cc_pipeline_graphics.cpp b/layers/core_checks/cc_pipeline_graphics.cpp index 705e72484eb..dfb1fb65b2c 100644 --- a/layers/core_checks/cc_pipeline_graphics.cpp +++ b/layers/core_checks/cc_pipeline_graphics.cpp @@ -106,6 +106,10 @@ bool CoreChecks::ValidateGraphicsPipeline(const vvl::Pipeline &pipeline, const v } } + if (enabled_features.multiviewPerViewViewports && rp_state->has_multiview_enabled) { + skip |= ValidateMultiviewPerViewViewports(pipeline, *rp_state, create_info_loc); + } + // Check for portability errors // Issue raised in https://gitlab.khronos.org/vulkan/vulkan/-/issues/3436 // The combination of GPL/DynamicRendering and Portability has spec issues that need to be clarified @@ -4268,6 +4272,47 @@ bool CoreChecks::ValidateMultiViewShaders(const vvl::Pipeline &pipeline, const L return skip; } +bool CoreChecks::ValidateMultiviewPerViewViewports(const vvl::Pipeline& pipeline, const vvl::RenderPass& rp_state, + const Location& create_info_loc) const { + bool skip = false; + const auto* viewport_state = pipeline.ViewportState(); + if (!viewport_state) { + return skip; + } + + if (!pipeline.IsDynamic(CB_DYNAMIC_STATE_VIEWPORT) && !pipeline.IsDynamic(CB_DYNAMIC_STATE_VIEWPORT_WITH_COUNT)) { + for (uint32_t i = 0; i < rp_state.create_info.subpassCount; i++) { + const uint32_t view_mask = rp_state.create_info.pSubpasses[i].viewMask; + const uint32_t msb = (uint32_t)MostSignificantBit(view_mask); + if (msb >= viewport_state->viewportCount) { + skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07730", rp_state.Handle(), + create_info_loc.dot(Field::renderPass), + "was created with VkRenderPassMultiviewCreateInfo::pViewMask[%" PRIu32 "] of 0x%" PRIx32 + " which most significant bit index (%" PRIu32 + ") is not less than pViewportState->viewportCount (%" PRIu32 ")", + i, view_mask, msb, viewport_state->viewportCount); + } + } + } + + if (!pipeline.IsDynamic(CB_DYNAMIC_STATE_SCISSOR) && !pipeline.IsDynamic(CB_DYNAMIC_STATE_SCISSOR_WITH_COUNT)) { + for (uint32_t i = 0; i < rp_state.create_info.subpassCount; i++) { + const uint32_t view_mask = rp_state.create_info.pSubpasses[i].viewMask; + const uint32_t msb = (uint32_t)MostSignificantBit(view_mask); + if (msb >= viewport_state->scissorCount) { + skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07731", rp_state.Handle(), + create_info_loc.dot(Field::renderPass), + "was created with VkRenderPassMultiviewCreateInfo::pViewMask[%" PRIu32 "] of 0x%" PRIx32 + " which most significant bit index (%" PRIu32 + ") is not less than pViewportState->scissorCount (%" PRIu32 ")", + i, view_mask, msb, viewport_state->scissorCount); + } + } + } + + return skip; +} + bool CoreChecks::ValidateDrawPipelineFramebuffer(const vvl::CommandBuffer &cb_state, const vvl::Pipeline &pipeline, const vvl::DrawDispatchVuid &vuid) const { bool skip = false; diff --git a/layers/core_checks/core_validation.h b/layers/core_checks/core_validation.h index d7982a38d25..d5a69196664 100644 --- a/layers/core_checks/core_validation.h +++ b/layers/core_checks/core_validation.h @@ -244,6 +244,8 @@ class CoreChecks : public vvl::DeviceProxy { bool ValidateComputePipelineDerivatives(PipelineStates& pipeline_states, uint32_t pipe_index, const Location& loc) const; bool ValidateMultiViewShaders(const vvl::Pipeline& pipeline, const Location& multiview_loc, uint32_t view_mask, bool dynamic_rendering) const; + bool ValidateMultiviewPerViewViewports(const vvl::Pipeline& pipeline, const vvl::RenderPass& rp_state, + const Location& create_info_loc) const; bool ValidateGraphicsPipeline(const vvl::Pipeline& pipeline, const void* pipeline_ci_pnext, const Location& create_info_loc) const; bool ValidImageBufferQueue(const vvl::CommandBuffer& cb_state, const VulkanTypedHandle& object, uint32_t queueFamilyIndex, diff --git a/tests/unit/multiview.cpp b/tests/unit/multiview.cpp index d5ada6051df..d5055f93732 100644 --- a/tests/unit/multiview.cpp +++ b/tests/unit/multiview.cpp @@ -1432,3 +1432,53 @@ TEST_F(NegativeMultiview, MeshShader) { pipe.CreateGraphicsPipeline(); m_errorMonitor->VerifyFound(); } + +TEST_F(NegativeMultiview, MultiviewPerViewViewports) { + SetTargetApiVersion(VK_API_VERSION_1_1); + AddRequiredExtensions(VK_KHR_MULTIVIEW_EXTENSION_NAME); + AddRequiredExtensions(VK_QCOM_MULTIVIEW_PER_VIEW_VIEWPORTS_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::multiview); + AddRequiredFeature(vkt::Feature::multiviewPerViewViewports); + RETURN_IF_SKIP(Init()); + InitRenderTarget(); + + uint32_t view_mask = 0x2u; + VkRenderPassMultiviewCreateInfo rp_mv_ci = vku::InitStructHelper(); + rp_mv_ci.subpassCount = 1; + rp_mv_ci.pViewMasks = &view_mask; + + RenderPassSingleSubpass rp(*this); + rp.AddAttachmentDescription(VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED); + rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL}); + rp.AddColorAttachment(0); + rp.CreateRenderPass(&rp_mv_ci); + + VkImageCreateInfo image_create_info = vku::InitStructHelper(); + image_create_info.flags = 0u; + image_create_info.imageType = VK_IMAGE_TYPE_2D; + image_create_info.format = VK_FORMAT_B8G8R8A8_UNORM; + image_create_info.extent = {32u, 32u, 1u}; + image_create_info.mipLevels = 1u; + image_create_info.arrayLayers = 2u; + image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; + image_create_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + vkt::Image image(*m_device, image_create_info, vkt::set_layout); + vkt::ImageView image_view = image.CreateView(VK_IMAGE_VIEW_TYPE_2D_ARRAY); + + vkt::Framebuffer framebuffer(*m_device, rp, 1, &image_view.handle()); + + VkRenderPassBeginInfo render_pass_bi = vku::InitStructHelper(); + render_pass_bi.renderPass = rp; + render_pass_bi.framebuffer = framebuffer; + render_pass_bi.renderArea.extent = {32u, 32u}; + render_pass_bi.clearValueCount = 1u; + render_pass_bi.pClearValues = m_renderPassClearValues.data(); + + CreatePipelineHelper pipe(*this); + pipe.gp_ci_.renderPass = rp; + m_errorMonitor->SetDesiredError("VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07730"); + m_errorMonitor->SetDesiredError("VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-07731"); + pipe.CreateGraphicsPipeline(); + m_errorMonitor->VerifyFound(); +} \ No newline at end of file