@@ -290,9 +290,11 @@ bool CoreChecks::PreCallValidateQueueSubmit(VkQueue queue, uint32_t submitCount,
290290 chained_device_group_struct->commandBufferCount , submit.commandBufferCount );
291291 }
292292 }
293- // Perform submit time validation at the end.
294- // If submit API is used incorrectly, we want those errors to be reported first
295- skip |= submit_time_tracker.ProcessSubmitInfo (submit, queue, submit_loc);
293+ // Run submit time validation only if skip is not set. This is mostly for vvl testing,
294+ // some tests expect that submit state is not updated if regular validation fails
295+ if (!skip) {
296+ skip |= submit_time_tracker.ProcessSubmitInfo (submit, queue, submit_loc);
297+ }
296298 }
297299
298300 return skip;
@@ -452,9 +454,11 @@ bool CoreChecks::ValidateQueueSubmit2(VkQueue queue, uint32_t submitCount, const
452454 skip |= LogError (" VUID-VkSubmitInfo2-commandBuffer-06010" , queue, submit_loc,
453455 " has a suspended render pass instance that was not resumed." );
454456 }
455- // Perform submit time validation at the end.
456- // If submit API is used incorrectly, we want those errors to be reported first
457- skip |= submit_time_tracker.ProcessSubmitInfo (submit, queue, submit_loc);
457+ // Run submit time validation only if skip is not set. This is mostly for vvl testing,
458+ // some tests expect that submit state is not updated if regular validation fails
459+ if (!skip) {
460+ skip |= submit_time_tracker.ProcessSubmitInfo (submit, queue, submit_loc);
461+ }
458462 }
459463
460464 return skip;
@@ -783,8 +787,21 @@ bool CoreChecks::PreCallValidateQueueBindSparse(VkQueue queue, uint32_t bindInfo
783787 return skip;
784788}
785789
786- bool CoreChecks::ProcessSubmissionBatch (const std::vector<std::shared_ptr<vvl::CommandBuffer>>& command_buffers,
787- const Location& submit_loc) {
790+ static Location GetSignaledSemaphoreLocation (const Location& submit_loc, uint32_t index) {
791+ vvl::Field field = vvl::Field::Empty;
792+ if (submit_loc.function == vvl::Func::vkQueueSubmit || submit_loc.function == vvl::Func::vkQueueBindSparse) {
793+ field = vvl::Field::pSignalSemaphores;
794+ } else if (submit_loc.function == vvl::Func::vkQueueSubmit2 || submit_loc.function == vvl::Func::vkQueueSubmit2KHR) {
795+ field = vvl::Field::pSignalSemaphoreInfos;
796+ } else {
797+ assert (false && " Unhandled signaling function" );
798+ }
799+ return submit_loc.dot (field, index);
800+ }
801+
802+ bool CoreChecks::ProcessSubmissionBatch (const vvl::SubmitTimeTracker& tracker,
803+ const std::vector<std::shared_ptr<vvl::CommandBuffer>>& command_buffers,
804+ vvl::span<const VkSemaphoreSubmitInfo> signal_semaphores, const Location& submit_loc) {
788805 bool skip = false ;
789806 // Validate image layouts on the command buffer boundaries
790807 {
@@ -796,6 +813,27 @@ bool CoreChecks::ProcessSubmissionBatch(const std::vector<std::shared_ptr<vvl::C
796813 }
797814 }
798815 }
816+ // Ensure that timeline signals are monotonically increasing values
817+ for (uint32_t i = 0 ; i < (uint32_t )signal_semaphores.size (); ++i) {
818+ const VkSemaphore semaphore = signal_semaphores[i].semaphore ;
819+ const std::optional<uint64_t > current_value = tracker.GetTimelineValue (semaphore);
820+ if (!current_value.has_value ()) {
821+ continue ;
822+ }
823+ // Validate the case where the signal value is less than the current payload.
824+ // Equality, which is also invalid, is handled earlier by SemaphoreSubmitState::ValidateTimelineSignal.
825+ // Equality can be checked early because it is invalid regardless submit order.
826+ // For not equal values, the comparison depends on submit ordering and is handled here
827+ const uint64_t signal_value = signal_semaphores[i].value ;
828+ const bool invlid_signal_value = signal_value < *current_value;
829+ if (invlid_signal_value) {
830+ const Location signal_semaphore_loc = GetSignaledSemaphoreLocation (submit_loc, i);
831+ const auto & vuid = GetQueueSubmitVUID (signal_semaphore_loc, vvl::SubmitError::kTimelineSemSmallValue );
832+ LogError (vuid, semaphore, signal_semaphore_loc,
833+ " (%s) signaled with value %" PRIu64 " which is smaller than the current value %" PRIu64,
834+ FormatHandle (semaphore).c_str (), signal_value, *current_value);
835+ }
836+ }
799837 if (!skip) {
800838 for (const auto & cb : command_buffers) {
801839 if (cb) {
0 commit comments