From 41b911d1d0e34a2e6a97a77954caf40f3b24b7cb Mon Sep 17 00:00:00 2001 From: Aliya Pazylbekova Date: Wed, 29 Apr 2020 18:43:46 -0400 Subject: [PATCH] Vk Pixel History: MSAA depth/stencil copy, shader out Use a separate compute shader module for MSAA copy, and output directly into the destination buffer instead of creating staging resources. Support case where there is no depth stencil attachment to get post mod values in per fragment reporting. Previously used the original framebuffer that might not have had depth/stencil view, so couldn't count the fragments. Now use the sub image. To get the post mod color, we need to blend with the premod color, so we use vkCmdCopyImage to copy from the original image. --- renderdoc/CMakeLists.txt | 1 + renderdoc/data/embedded_files.h | 1 + renderdoc/data/glsl/pixelhistory_mscopy.comp | 67 +++ renderdoc/data/renderdoc.rc | 1 + renderdoc/data/resource.h | 5 +- renderdoc/driver/vulkan/vk_debug.cpp | 11 +- renderdoc/driver/vulkan/vk_debug.h | 2 - .../driver/vulkan/vk_msaa_array_conv.cpp | 21 - renderdoc/driver/vulkan/vk_pixelhistory.cpp | 544 ++++++++++-------- renderdoc/driver/vulkan/vk_replay.cpp | 29 +- renderdoc/driver/vulkan/vk_replay.h | 10 +- renderdoc/driver/vulkan/vk_shader_cache.cpp | 2 + renderdoc/driver/vulkan/vk_shader_cache.h | 1 + renderdoc/renderdoc.vcxproj | 1 + util/test/demos/vk/vk_pixel_history_test.cpp | 6 + util/test/tests/Vulkan/VK_Pixel_History.py | 8 +- 16 files changed, 435 insertions(+), 275 deletions(-) create mode 100644 renderdoc/data/glsl/pixelhistory_mscopy.comp diff --git a/renderdoc/CMakeLists.txt b/renderdoc/CMakeLists.txt index 6a63c80d6..83706cbd1 100644 --- a/renderdoc/CMakeLists.txt +++ b/renderdoc/CMakeLists.txt @@ -385,6 +385,7 @@ set(data data/glsl/minmaxtile.comp data/glsl/quadresolve.frag data/glsl/quadwrite.frag + data/glsl/pixelhistory_mscopy.comp data/glsl/pixelhistory_primid.frag data/glsl/shaderdebug_sample.vert data/glsl/texdisplay.frag diff --git a/renderdoc/data/embedded_files.h b/renderdoc/data/embedded_files.h index 81b70dae7..b3f2a3252 100644 --- a/renderdoc/data/embedded_files.h +++ b/renderdoc/data/embedded_files.h @@ -63,6 +63,7 @@ DECLARE_EMBED(glsl_ms2array_comp); DECLARE_EMBED(glsl_deptharr2ms_frag); DECLARE_EMBED(glsl_depthms2arr_frag); DECLARE_EMBED(glsl_gles_texsample_h); +DECLARE_EMBED(glsl_pixelhistory_mscopy_comp); DECLARE_EMBED(glsl_pixelhistory_primid_frag); DECLARE_EMBED(glsl_shaderdebug_sample_vert); DECLARE_EMBED(glsl_texremap_frag); diff --git a/renderdoc/data/glsl/pixelhistory_mscopy.comp b/renderdoc/data/glsl/pixelhistory_mscopy.comp new file mode 100644 index 000000000..2e6927349 --- /dev/null +++ b/renderdoc/data/glsl/pixelhistory_mscopy.comp @@ -0,0 +1,67 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2020 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "glsl_globals.h" + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0) uniform PRECISION usampler2DMSArray srcMS0; +layout(binding = 1) uniform PRECISION usampler2DMSArray srcMS1; + +layout(binding = 2, std140) writeonly buffer pixelhistorydest +{ + uvec4 result[]; +} +dest; + +layout(push_constant) uniform multisamplePush +{ + int depthCopy; + int currentSample; + int x; + int y; + int dstOffset; +} +mscopy; + +#define depthCopy (mscopy.depthCopy) +#define currentSample (mscopy.currentSample) +#define x (mscopy.x) +#define y (mscopy.y) +#define dstOffset (mscopy.dstOffset) + +void main() +{ + if(depthCopy == 0) + { + uvec4 data = texelFetch(srcMS0, ivec3(x, y, 0), currentSample); + dest.result[dstOffset] = data; + } + else if(depthCopy == 1) + { + uint depth = texelFetch(srcMS0, ivec3(x, y, 0), currentSample).r; + uint stencil = texelFetch(srcMS1, ivec3(x, y, 0), currentSample).r; + dest.result[dstOffset] = uvec4(depth, stencil, 0, 0); + } +} diff --git a/renderdoc/data/renderdoc.rc b/renderdoc/data/renderdoc.rc index 68bb0da06..264df0ac3 100644 --- a/renderdoc/data/renderdoc.rc +++ b/renderdoc/data/renderdoc.rc @@ -128,6 +128,7 @@ RESOURCE_glsl_mesh_frag TYPE_EMBED "glsl/mesh.frag" RESOURCE_glsl_minmaxtile_comp TYPE_EMBED "glsl/minmaxtile.comp" RESOURCE_glsl_minmaxresult_comp TYPE_EMBED "glsl/minmaxresult.comp" RESOURCE_glsl_histogram_comp TYPE_EMBED "glsl/histogram.comp" +RESOURCE_glsl_pixelhistory_mscopy_comp TYPE_EMBED "glsl/pixelhistory_mscopy.comp" RESOURCE_glsl_pixelhistory_primid_frag TYPE_EMBED "glsl/pixelhistory_primid.frag" RESOURCE_glsl_glsl_ubos_h TYPE_EMBED "glsl/glsl_ubos.h" RESOURCE_glsl_gl_texsample_h TYPE_EMBED "glsl/gl_texsample.h" diff --git a/renderdoc/data/resource.h b/renderdoc/data/resource.h index 523680371..45eb5b9be 100644 --- a/renderdoc/data/resource.h +++ b/renderdoc/data/resource.h @@ -48,8 +48,9 @@ #define RESOURCE_glsl_gltext_frag 430 #define RESOURCE_glsl_glsl_globals_h 440 #define RESOURCE_glsl_texremap_frag 441 -#define RESOURCE_glsl_pixelhistory_primid_frag 442 -#define RESOURCE_glsl_shaderdebug_sample_vert 443 +#define RESOURCE_glsl_pixelhistory_mscopy_comp 442 +#define RESOURCE_glsl_pixelhistory_primid_frag 443 +#define RESOURCE_glsl_shaderdebug_sample_vert 444 // Next default values for new objects // diff --git a/renderdoc/driver/vulkan/vk_debug.cpp b/renderdoc/driver/vulkan/vk_debug.cpp index e6b46d058..f747e5d4f 100644 --- a/renderdoc/driver/vulkan/vk_debug.cpp +++ b/renderdoc/driver/vulkan/vk_debug.cpp @@ -2644,13 +2644,22 @@ void VulkanReplay::PixelHistory::Init(WrappedVulkan *driver, VkDescriptorPool de { {0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL}, {1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL}, - {2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_ALL, NULL}, + {2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, NULL}, }); CREATE_OBJECT(MSCopyDescSet, descriptorPool, MSCopyDescSetLayout); + CREATE_OBJECT(MSDepthCopyDescSet, descriptorPool, MSCopyDescSetLayout); + + CREATE_OBJECT(MSCopyPipeLayout, MSCopyDescSetLayout, 32); + CREATE_OBJECT(MSCopyPipe, MSCopyPipeLayout, + driver->GetShaderCache()->GetBuiltinModule(BuiltinShader::PixelHistoryMSCopyCS)); } void VulkanReplay::PixelHistory::Destroy(WrappedVulkan *driver) { + if(MSCopyPipe != VK_NULL_HANDLE) + driver->vkDestroyPipeline(driver->GetDev(), MSCopyPipe, NULL); + if(MSCopyPipeLayout != VK_NULL_HANDLE) + driver->vkDestroyPipelineLayout(driver->GetDev(), MSCopyPipeLayout, NULL); if(MSCopyDescSetLayout != VK_NULL_HANDLE) driver->vkDestroyDescriptorSetLayout(driver->GetDev(), MSCopyDescSetLayout, NULL); } diff --git a/renderdoc/driver/vulkan/vk_debug.h b/renderdoc/driver/vulkan/vk_debug.h index fa84eea94..e5ce26e70 100644 --- a/renderdoc/driver/vulkan/vk_debug.h +++ b/renderdoc/driver/vulkan/vk_debug.h @@ -68,8 +68,6 @@ public: uint32_t samples, VkFormat fmt); void CopyArrayToTex2DMS(VkImage destMS, VkImage srcArray, VkExtent3D extent, uint32_t layers, uint32_t samples, VkFormat fmt); - void CopyTex2DMSPixel(VkCommandBuffer cmd, VkDescriptorSet descSet, VkExtent3D extent, - uint32_t sample, VkFormat fmt); VkPipelineCache GetPipelineCache() { return m_PipelineCache; } VkPipeline GetCustomPipeline() { return m_Custom.TexPipeline; } diff --git a/renderdoc/driver/vulkan/vk_msaa_array_conv.cpp b/renderdoc/driver/vulkan/vk_msaa_array_conv.cpp index 13778f365..f00900076 100644 --- a/renderdoc/driver/vulkan/vk_msaa_array_conv.cpp +++ b/renderdoc/driver/vulkan/vk_msaa_array_conv.cpp @@ -146,27 +146,6 @@ void VulkanDebugManager::CopyTex2DMSToArray(VkImage destArray, VkImage srcMS, Vk ObjDisp(dev)->DestroyImageView(Unwrap(dev), destView, NULL); } -void VulkanDebugManager::CopyTex2DMSPixel(VkCommandBuffer cmd, VkDescriptorSet descSet, - VkExtent3D extent, uint32_t sample, VkFormat fmt) -{ - if(!m_pDriver->GetDeviceFeatures().shaderStorageImageWriteWithoutFormat) - return; - - if(m_MS2ArrayPipe == VK_NULL_HANDLE) - return; - - ObjDisp(cmd)->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_COMPUTE, Unwrap(m_MS2ArrayPipe)); - ObjDisp(cmd)->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_COMPUTE, - Unwrap(m_ArrayMSPipeLayout), 0, 1, UnwrapPtr(descSet), 0, NULL); - - Vec4u params = {0, sample, 0, 0}; - - ObjDisp(cmd)->CmdPushConstants(Unwrap(cmd), Unwrap(m_ArrayMSPipeLayout), VK_SHADER_STAGE_ALL, 0, - sizeof(Vec4u), ¶ms); - // TODO: Specify a single pixel to copy. - ObjDisp(cmd)->CmdDispatch(Unwrap(cmd), extent.width, extent.height, 1); -} - void VulkanDebugManager::CopyDepthTex2DMSToArray(VkImage destArray, VkImage srcMS, VkExtent3D extent, uint32_t layers, uint32_t samples, VkFormat fmt) { diff --git a/renderdoc/driver/vulkan/vk_pixelhistory.cpp b/renderdoc/driver/vulkan/vk_pixelhistory.cpp index 2c68495d3..ad875020f 100644 --- a/renderdoc/driver/vulkan/vk_pixelhistory.cpp +++ b/renderdoc/driver/vulkan/vk_pixelhistory.cpp @@ -61,7 +61,6 @@ enum struct CopyPixelParams { bool depthCopy; - bool stencilOnly; VkImage srcImage; VkFormat srcImageFormat; VkImageLayout srcImageLayout; @@ -75,16 +74,21 @@ struct PixelHistoryResources // Used for offscreen rendering for draw call events. VkImage colorImage; VkImageView colorImageView; - VkImage stencilImage; - VkImageView stencilImageView; + VkImage dsImage; + VkImageView dsImageView; VkDeviceMemory gpuMem; // Following are only used and created for multi sampled images. - VkImage stagingImage; - VkImageView stagingImageView; - VkImage stencilStagingImage; - VkImageView stencilStagingImageView; + // This is an image view for colorImage which uses a UINT format. + VkImageView colorImageAliasView; + // Image view for target image which uses a UINT format. VkImageView targetImageView; + // Image view for dsImage depth stencil image that includes depth + // aspect only. + VkImageView depthOnlyImageView; + // Image view for dsImage depth stencil image that includes stencil + // aspect only. + VkImageView stencilOnlyImageView; }; struct PixelHistoryCallbackInfo @@ -108,12 +112,8 @@ struct PixelHistoryCallbackInfo VkImageView subImageView; // Image used to get stencil counts. - VkImage stencilImage; - VkImageView stencilImageView; - - // Only used for multi sampled images for copy into a staging resource. - VkImage stagingImage; - VkImage stencilStagingImage; + VkImage dsImage; + VkImageView dsImageView; // Buffer used to copy colour and depth information VkBuffer dstBuffer; @@ -129,7 +129,7 @@ struct PixelHistoryValue float fdepth; } depth; int8_t stencil; - uint8_t padding[3]; + uint8_t padding[3 + 8]; }; struct EventInfo @@ -137,7 +137,9 @@ struct EventInfo PixelHistoryValue premod; PixelHistoryValue postmod; uint8_t dsWithoutShaderDiscard[8]; + uint32_t padding[4]; uint8_t dsWithShaderDiscard[8]; + uint32_t padding1[4]; }; struct PerFragmentInfo @@ -693,118 +695,156 @@ protected: void CopyImagePixel(VkCommandBuffer cmd, CopyPixelParams &p, size_t offset) { - rdcarray regions; VkImageAspectFlags aspectFlags = 0; - VkBufferImageCopy region = {}; - region.bufferOffset = (uint64_t)offset; - region.bufferRowLength = 0; - region.bufferImageHeight = 0; - region.imageOffset.x = m_CallbackInfo.x; - region.imageOffset.y = m_CallbackInfo.y; - region.imageOffset.z = 0; - region.imageExtent.width = 1U; - region.imageExtent.height = 1U; - region.imageExtent.depth = 1U; - region.imageSubresource.baseArrayLayer = m_CallbackInfo.targetSubresource.slice; - region.imageSubresource.mipLevel = m_CallbackInfo.targetSubresource.mip; - region.imageSubresource.layerCount = 1; - - // TODO: support depth/stencil copy for multi-sampled images. - if(p.depthCopy && (m_CallbackInfo.samples != VK_SAMPLE_COUNT_1_BIT)) - return; - - if(!p.depthCopy) + if(p.depthCopy) { - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - regions.push_back(region); - aspectFlags = VkImageAspectFlags(VK_IMAGE_ASPECT_COLOR_BIT); - } - else if(p.stencilOnly) - { - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; - regions.push_back(region); - aspectFlags = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + if(IsDepthOnlyFormat(p.srcImageFormat) || IsDepthAndStencilFormat(p.srcImageFormat)) + aspectFlags |= VK_IMAGE_ASPECT_DEPTH_BIT; + if(IsStencilFormat(p.srcImageFormat)) + aspectFlags |= VK_IMAGE_ASPECT_STENCIL_BIT; } else { - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - if(IsDepthOnlyFormat(p.srcImageFormat) || IsDepthAndStencilFormat(p.srcImageFormat)) - { - regions.push_back(region); - aspectFlags |= VK_IMAGE_ASPECT_DEPTH_BIT; - } - if(IsStencilFormat(p.srcImageFormat)) - { - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; - region.bufferOffset = offset + 4; - regions.push_back(region); - aspectFlags |= VK_IMAGE_ASPECT_STENCIL_BIT; - } + aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT; } - VkImage cmdCopySource = p.srcImage; - VkImageLayout cmdCopySourceLayout = p.srcImageLayout; - uint32_t baseArrayLayer = m_CallbackInfo.targetSubresource.slice; + uint32_t baseMip = m_CallbackInfo.targetSubresource.mip; + uint32_t baseSlice = m_CallbackInfo.targetSubresource.slice; + // The images that are created specifically for evaluating pixel history are + // already based on the target mip/slice. + if((p.srcImage == m_CallbackInfo.subImage) || (p.srcImage == m_CallbackInfo.dsImage)) + { + baseMip = 0; + baseSlice = 0; + } + // For pipeline barriers. + VkImageSubresourceRange subresource = {aspectFlags, baseMip, 1, baseSlice, 1}; // For multi-sampled images can't call vkCmdCopyImageToBuffer directly, // copy using a compute shader into a staging image first. if(m_CallbackInfo.samples != VK_SAMPLE_COUNT_1_BIT) { - regions[0].imageSubresource.baseArrayLayer = 0; - VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - NULL, - VK_ACCESS_SHADER_WRITE_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | - VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_MEMORY_WRITE_BIT, - VK_ACCESS_SHADER_READ_BIT, - p.srcImageLayout, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED, - Unwrap(p.srcImage), - {aspectFlags, 0, 1, baseArrayLayer, 1}}; + VkImageMemoryBarrier barrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, + VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | + VK_ACCESS_MEMORY_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT, p.srcImageLayout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, Unwrap(p.srcImage), subresource}; + if(p.depthCopy && p.srcImage != m_CallbackInfo.dsImage) + { + // This is an original depth image that is used in a draw. + // The descriptor for MSAA copy has the dsImage created for pixel history. + // So copy the pixel value there first. + VkImageCopy region = {}; + region.srcSubresource = {aspectFlags, baseMip, baseSlice, 1}; + region.srcOffset = {(int32_t)m_CallbackInfo.x, (int32_t)m_CallbackInfo.y, 0}; + region.dstSubresource = {aspectFlags, 0, 0, 1}; + region.dstOffset = {(int32_t)m_CallbackInfo.x, (int32_t)m_CallbackInfo.y, 0}; + region.extent = {1, 1, 1}; + + VkImageMemoryBarrier barriers[2]; + barriers[0] = barrier; + barriers[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barriers[0].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + barriers[1] = barriers[0]; + barriers[1].image = Unwrap(m_CallbackInfo.dsImage); + barriers[1].oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + barriers[1].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + + DoPipelineBarrier(cmd, 2, barriers); + + ObjDisp(cmd)->CmdCopyImage( + Unwrap(cmd), Unwrap(p.srcImage), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + Unwrap(m_CallbackInfo.dsImage), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + // Return src image to its layout. + barrier.image = Unwrap(p.srcImage); + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.newLayout = p.srcImageLayout; + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.dstAccessMask = VK_ACCESS_ALL_WRITE_BITS; + DoPipelineBarrier(cmd, 1, &barrier); + + barrier.image = Unwrap(m_CallbackInfo.dsImage); + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + } + + // Transition src image to SHADER_READ_ONLY_OPTIMAL. DoPipelineBarrier(cmd, 1, &barrier); - m_pDriver->GetReplay()->CopyPixelForPixelHistory(cmd, m_CallbackInfo.extent, - m_CallbackInfo.targetSubresource.sample, - m_CallbackInfo.targetImageFormat); + m_pDriver->GetReplay()->CopyPixelForPixelHistory( + cmd, {(int32_t)m_CallbackInfo.x, (int32_t)m_CallbackInfo.y}, + m_CallbackInfo.targetSubresource.sample, (uint32_t)offset / 16, p.depthCopy); + + // Transition src image back to its layout. barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; barrier.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; barrier.newLayout = p.srcImageLayout; + + DoPipelineBarrier(cmd, 1, &barrier); + } + else + { + rdcarray regions; + VkBufferImageCopy region = {}; + region.bufferOffset = (uint64_t)offset; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + region.imageOffset.x = m_CallbackInfo.x; + region.imageOffset.y = m_CallbackInfo.y; + region.imageOffset.z = 0; + region.imageExtent.width = 1U; + region.imageExtent.height = 1U; + region.imageExtent.depth = 1U; + region.imageSubresource.baseArrayLayer = baseSlice; + region.imageSubresource.mipLevel = baseMip; + region.imageSubresource.layerCount = 1; + + if(!p.depthCopy) + { + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + regions.push_back(region); + } + else + { + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + if(IsDepthOnlyFormat(p.srcImageFormat) || IsDepthAndStencilFormat(p.srcImageFormat)) + { + regions.push_back(region); + } + if(IsStencilFormat(p.srcImageFormat)) + { + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; + region.bufferOffset = offset + 4; + regions.push_back(region); + } + } + + VkImageMemoryBarrier barrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, + VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | + VK_ACCESS_MEMORY_WRITE_BIT, + VK_ACCESS_TRANSFER_READ_BIT, p.srcImageLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, Unwrap(p.srcImage), subresource}; DoPipelineBarrier(cmd, 1, &barrier); - cmdCopySource = m_CallbackInfo.stagingImage; - cmdCopySourceLayout = VK_IMAGE_LAYOUT_GENERAL; - baseArrayLayer = 0; + ObjDisp(cmd)->CmdCopyImageToBuffer( + Unwrap(cmd), Unwrap(p.srcImage), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + Unwrap(m_CallbackInfo.dstBuffer), (uint32_t)regions.size(), regions.data()); + + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.newLayout = p.srcImageLayout; + DoPipelineBarrier(cmd, 1, &barrier); } - - VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - NULL, - VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | - VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_MEMORY_WRITE_BIT, - VK_ACCESS_TRANSFER_READ_BIT, - cmdCopySourceLayout, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED, - Unwrap(cmdCopySource), - {aspectFlags, 0, 1, baseArrayLayer, 1}}; - - DoPipelineBarrier(cmd, 1, &barrier); - - ObjDisp(cmd)->CmdCopyImageToBuffer( - Unwrap(cmd), Unwrap(cmdCopySource), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - Unwrap(m_CallbackInfo.dstBuffer), (uint32_t)regions.size(), regions.data()); - - barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - barrier.newLayout = cmdCopySourceLayout; - DoPipelineBarrier(cmd, 1, &barrier); } WrappedVulkan *m_pDriver; @@ -1032,9 +1072,8 @@ struct VulkanColorAndStencilCallback : public VulkanPixelHistoryCallback { VkRenderPass newRp = CreateRenderPass(pipestate.renderPass, pipestate.GetFramebuffer(), pipestate.subpass); - VkFramebuffer newFb = - CreateFramebuffer(pipestate.renderPass, newRp, pipestate.subpass, - pipestate.GetFramebuffer(), m_CallbackInfo.stencilImageView); + VkFramebuffer newFb = CreateFramebuffer(pipestate.renderPass, newRp, pipestate.subpass, + pipestate.GetFramebuffer(), m_CallbackInfo.dsImageView); uint32_t framebufferIndex = 0; const rdcarray &atts = pipestate.GetFramebufferAttachments(); for(uint32_t i = 0; i < atts.size(); i++) @@ -1065,11 +1104,10 @@ struct VulkanColorAndStencilCallback : public VulkanPixelHistoryCallback ReplayDraw(cmd, eid, true); CopyPixelParams params = {}; - params.srcImage = m_CallbackInfo.stencilImage; + params.srcImage = m_CallbackInfo.dsImage; params.srcImageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; params.srcImageFormat = VK_FORMAT_D32_SFLOAT_S8_UINT; params.depthCopy = true; - params.stencilOnly = true; // Copy stencil value that indicates the number of fragments ignoring // shader discard. CopyImagePixel(cmd, params, storeOffset + offsetof(struct EventInfo, dsWithoutShaderDiscard)); @@ -1867,9 +1905,11 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback { VulkanPixelHistoryPerFragmentCallback(WrappedVulkan *vk, PixelHistoryShaderCache *shaderCache, const PixelHistoryCallbackInfo &callbackInfo, - std::map eventFragments) + const std::map &eventFragments, + const std::map &eventPremods) : VulkanPixelHistoryCallback(vk, shaderCache, callbackInfo, VK_NULL_HANDLE), - m_EventFragments(eventFragments) + m_EventFragments(eventFragments), + m_EventPremods(eventPremods) { } @@ -1907,7 +1947,7 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback VkFramebuffer newFb = CreateFramebuffer(state.renderPass, newRp, state.subpass, state.GetFramebuffer(), - m_CallbackInfo.stencilImageView, m_CallbackInfo.subImageView); + m_CallbackInfo.dsImageView, m_CallbackInfo.subImageView); uint32_t framebufferIndex = 0; const rdcarray &atts = prevState.GetFramebufferAttachments(); @@ -1921,7 +1961,7 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback } } - Pipelines pipes = CreatePipelines(curPipeline, newRp, eid, false, 0, framebufferIndex); + Pipelines pipes = CreatePerFragmentPipelines(curPipeline, newRp, eid, false, 0, framebufferIndex); state.renderPass = GetResID(newRp); state.SetFramebuffer(m_pDriver, GetResID(newFb)); @@ -1932,9 +1972,12 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback CopyPixelParams colourCopyParams = {}; colourCopyParams.srcImage = m_CallbackInfo.subImage; - colourCopyParams.srcImageLayout = - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // TODO: image layout - colourCopyParams.srcImageFormat = VK_FORMAT_R32G32B32A32_SFLOAT; + // Use the layout of the image we are substituting for. + VkImageLayout srcImageLayout = m_pDriver->GetDebugManager()->GetImageLayout( + GetResID(m_CallbackInfo.targetImage), VK_IMAGE_ASPECT_COLOR_BIT, + m_CallbackInfo.targetSubresource.mip, m_CallbackInfo.targetSubresource.slice); + colourCopyParams.srcImageLayout = srcImageLayout; + colourCopyParams.srcImageFormat = m_CallbackInfo.targetImageFormat; const VulkanCreationInfo::Pipeline &p = m_pDriver->GetDebugManager()->GetPipelineInfo(prevState.graphics.pipeline); @@ -1954,7 +1997,7 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, - Unwrap(m_CallbackInfo.stencilImage), + Unwrap(m_CallbackInfo.dsImage), {VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, 0, 1, 0, 1}}; DoPipelineBarrier(cmd, 1, &barrier); @@ -1971,7 +2014,7 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback range.baseMipLevel = 0; range.layerCount = 1; range.levelCount = 1; - ObjDisp(cmd)->CmdClearDepthStencilImage(Unwrap(cmd), Unwrap(m_CallbackInfo.stencilImage), + ObjDisp(cmd)->CmdClearDepthStencilImage(Unwrap(cmd), Unwrap(m_CallbackInfo.dsImage), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &dsValue, 1, &range); @@ -2007,7 +2050,7 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback { CopyPixelParams depthCopyParams = colourCopyParams; depthCopyParams.depthCopy = true; - depthCopyParams.srcImage = m_CallbackInfo.stencilImage; + depthCopyParams.srcImage = m_CallbackInfo.dsImage; depthCopyParams.srcImageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; depthCopyParams.srcImageFormat = VK_FORMAT_D32_SFLOAT_S8_UINT; CopyImagePixel(cmd, depthCopyParams, @@ -2029,13 +2072,7 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback depthFormat = imginfo.format; } - // Restore the original framebuffer and render pass, so that we can get - // post modification values. - state.SetFramebuffer(prevState.GetFramebuffer(), prevState.GetFramebufferAttachments()); - state.renderPass = prevState.renderPass; - colourCopyParams.srcImage = m_CallbackInfo.targetImage; - colourCopyParams.srcImageFormat = m_CallbackInfo.targetImageFormat; - + const ModificationValue &premod = m_EventPremods[eid]; // For every fragment except the last one, retrieve post-modification // value. for(uint32_t f = 0; f < numFragmentsInEvent - 1; f++) @@ -2054,6 +2091,23 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback rect.baseArrayLayer = 0; rect.layerCount = 1; ObjDisp(cmd)->CmdClearAttachments(Unwrap(cmd), 1, &att, 1, &rect); + + if(f == 0) + { + // Before starting the draw, initialize the pixel to the premodification value + // for this event, for both color and stencil. + VkClearAttachment clearAtts[2] = {}; + clearAtts[0].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + clearAtts[0].colorAttachment = framebufferIndex; + memcpy(clearAtts[0].clearValue.color.float32, premod.col.floatValue, + sizeof(clearAtts[0].clearValue.color)); + + clearAtts[1].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + clearAtts[1].clearValue.depthStencil.depth = premod.depth; + + ObjDisp(cmd)->CmdClearAttachments(Unwrap(cmd), 2, clearAtts, 1, &rect); + } + ObjDisp(cmd)->CmdSetStencilReference(Unwrap(cmd), VK_STENCIL_FACE_FRONT_AND_BACK, f); const DrawcallDescription *drawcall = m_pDriver->GetDrawcall(eid); if(drawcall->flags & DrawFlags::Indexed) @@ -2072,7 +2126,7 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback { CopyPixelParams depthCopyParams = colourCopyParams; depthCopyParams.depthCopy = true; - depthCopyParams.srcImage = depthImage; + depthCopyParams.srcImage = m_CallbackInfo.dsImage; depthCopyParams.srcImageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; depthCopyParams.srcImageFormat = depthFormat; CopyImagePixel(cmd, depthCopyParams, (fragsProcessed + f) * sizeof(PerFragmentInfo) + @@ -2090,9 +2144,10 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback } bool PostDraw(uint32_t eid, VkCommandBuffer cmd) { return false; } void PostRedraw(uint32_t eid, VkCommandBuffer cmd) {} - // CreatePipelines for getting per fragment information. - Pipelines CreatePipelines(ResourceId pipe, VkRenderPass rp, uint32_t eid, bool dynamicScissor, - uint32_t fragmentIndex, uint32_t framebufferIndex) + // CreatePerFragmentPipelines for getting per fragment information. + Pipelines CreatePerFragmentPipelines(ResourceId pipe, VkRenderPass rp, uint32_t eid, + bool dynamicScissor, uint32_t fragmentIndex, + uint32_t framebufferIndex) { const VulkanCreationInfo::Pipeline &p = m_pDriver->GetDebugManager()->GetPipelineInfo(pipe); VkGraphicsPipelineCreateInfo pipeCreateInfo = {}; @@ -2178,6 +2233,8 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback dynState->pDynamicStates = dynamicStates.data(); } + pipeCreateInfo.renderPass = rp; + Pipelines pipes = {}; VkResult vkr = m_pDriver->vkCreateGraphicsPipelines(m_pDriver->GetDev(), VK_NULL_HANDLE, 1, &pipeCreateInfo, NULL, &pipes.postModPipe); @@ -2189,6 +2246,11 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback // Disable some tests, leave depthTest and depthWriteEnable as is. // If we disable depth test, depth information would not be written. { + // TODO: this causes post-modification values to be incorrectly mapped to events + // if some fragments failed due to culling. We are disabling culling for reporting + // shader output values and events, but if some events failed culling when we + // replay to get post-modification values the stencil count will not reach the + // expected stencil count, since stencil is not updated if culling failed. rs->cullMode = VK_CULL_MODE_NONE; rs->rasterizerDiscardEnable = VK_FALSE; ds->depthBoundsTestEnable = VK_FALSE; @@ -2196,7 +2258,6 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback ds->depthCompareOp = VK_COMPARE_OP_ALWAYS; } - pipeCreateInfo.renderPass = rp; VkPipelineColorBlendStateCreateInfo *cbs = (VkPipelineColorBlendStateCreateInfo *)pipeCreateInfo.pColorBlendState; // Turn off blending so that we can get shader output values. @@ -2288,6 +2349,9 @@ private: std::map m_EventIndices; // Number of fragments for each event. std::map m_EventFragments; + // Pre-modification values for events to initialize attachments to, + // so that we can get blended post-modification values. + std::map m_EventPremods; // Number of fragments processed so far. uint32_t fragsProcessed = 0; @@ -2440,16 +2504,14 @@ bool VulkanDebugManager::PixelHistorySetupResources(PixelHistoryResources &resou { VkImage colorImage; VkImageView colorImageView; - VkImage stencilImage; - VkImageView stencilImageView; - VkImage stagingImage = VK_NULL_HANDLE; - VkImageView stagingImageView = VK_NULL_HANDLE; - VkDeviceSize stagingImageOffset = 0; - VkImage stencilStagingImage = VK_NULL_HANDLE; - VkImageView stencilStagingImageView = VK_NULL_HANDLE; - VkDeviceSize stencilStagingImageOffset = 0; + VkImage dsImage; + VkImageView dsImageView; + + VkImageView colorImageAliasView = VK_NULL_HANDLE; VkImageView targetImageView = VK_NULL_HANDLE; + VkImageView depthOnlyImageView = VK_NULL_HANDLE; + VkImageView stencilOnlyImageView = VK_NULL_HANDLE; VkDeviceMemory gpuMem; @@ -2476,7 +2538,10 @@ bool VulkanDebugManager::PixelHistorySetupResources(PixelHistoryResources &resou imgInfo.extent.height = extent.height; imgInfo.extent.depth = 1; imgInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imgInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + imgInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT; + if(samples != VK_SAMPLE_COUNT_1_BIT) + imgInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT; vkr = m_pDriver->vkCreateImage(dev, &imgInfo, NULL, &colorImage); RDCASSERTEQUAL(vkr, VK_SUCCESS); @@ -2491,41 +2556,16 @@ bool VulkanDebugManager::PixelHistorySetupResources(PixelHistoryResources &resou imgInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - vkr = m_pDriver->vkCreateImage(dev, &imgInfo, NULL, &stencilImage); + vkr = m_pDriver->vkCreateImage(dev, &imgInfo, NULL, &dsImage); RDCASSERTEQUAL(vkr, VK_SUCCESS); - ImageState stencilImageState = ImageState(stencilImage, ImageInfo(imgInfo), eFrameRef_None); + ImageState stencilImageState = ImageState(dsImage, ImageInfo(imgInfo), eFrameRef_None); VkMemoryRequirements stencilImageMrq = {0}; - m_pDriver->vkGetImageMemoryRequirements(dev, stencilImage, &stencilImageMrq); + m_pDriver->vkGetImageMemoryRequirements(dev, dsImage, &stencilImageMrq); VkDeviceSize offset = AlignUp(totalMemorySize, stencilImageMrq.alignment); totalMemorySize = offset + stencilImageMrq.size; - if(samples != VK_SAMPLE_COUNT_1_BIT) - { - imgInfo.format = format; - imgInfo.arrayLayers = 1; - imgInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imgInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - vkr = m_pDriver->vkCreateImage(dev, &imgInfo, NULL, &stagingImage); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - VkMemoryRequirements msImageMrq = {0}; - m_pDriver->vkGetImageMemoryRequirements(dev, stagingImage, &msImageMrq); - stagingImageOffset = AlignUp(totalMemorySize, msImageMrq.alignment); - totalMemorySize = stagingImageOffset + msImageMrq.size; - - imgInfo.format = VK_FORMAT_D32_SFLOAT_S8_UINT; - imgInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - vkr = m_pDriver->vkCreateImage(dev, &imgInfo, NULL, &stencilStagingImage); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - VkMemoryRequirements stencilStagingImageMrq = {0}; - m_pDriver->vkGetImageMemoryRequirements(dev, stencilStagingImage, &stencilStagingImageMrq); - stencilStagingImageOffset = AlignUp(totalMemorySize, stencilStagingImageMrq.alignment); - totalMemorySize = stencilStagingImageOffset + stencilStagingImageMrq.size; - } - VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, totalMemorySize, m_pDriver->GetGPULocalMemoryIndex(colorImageMrq.memoryTypeBits), @@ -2536,7 +2576,7 @@ bool VulkanDebugManager::PixelHistorySetupResources(PixelHistoryResources &resou vkr = m_pDriver->vkBindImageMemory(m_Device, colorImage, gpuMem, 0); RDCASSERTEQUAL(vkr, VK_SUCCESS); - vkr = m_pDriver->vkBindImageMemory(m_Device, stencilImage, gpuMem, offset); + vkr = m_pDriver->vkBindImageMemory(m_Device, dsImage, gpuMem, offset); RDCASSERTEQUAL(vkr, VK_SUCCESS); VkImageViewCreateInfo viewInfo = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; @@ -2545,26 +2585,21 @@ bool VulkanDebugManager::PixelHistorySetupResources(PixelHistoryResources &resou viewInfo.format = VK_FORMAT_R32G32B32A32_SFLOAT; viewInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + if(samples != VK_SAMPLE_COUNT_1_BIT) + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; + vkr = m_pDriver->vkCreateImageView(m_Device, &viewInfo, NULL, &colorImageView); RDCASSERTEQUAL(vkr, VK_SUCCESS); - viewInfo.image = stencilImage; + viewInfo.image = dsImage; viewInfo.format = VK_FORMAT_D32_SFLOAT_S8_UINT; viewInfo.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, 0, 1, 0, 1}; - vkr = m_pDriver->vkCreateImageView(m_Device, &viewInfo, NULL, &stencilImageView); + vkr = m_pDriver->vkCreateImageView(m_Device, &viewInfo, NULL, &dsImageView); RDCASSERTEQUAL(vkr, VK_SUCCESS); if(samples != VK_SAMPLE_COUNT_1_BIT) { - vkr = m_pDriver->vkBindImageMemory(m_Device, stagingImage, gpuMem, stagingImageOffset); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - viewInfo.image = stagingImage; - viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; - viewInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, - VK_REMAINING_ARRAY_LAYERS}; - uint32_t bs = GetByteSize(1, 1, 1, format, 0); if(bs == 1) @@ -2583,30 +2618,29 @@ bool VulkanDebugManager::PixelHistorySetupResources(PixelHistoryResources &resou RDCERR("Can't copy 2D to Array with format %s", ToStr(format).c_str()); } - vkr = m_pDriver->vkCreateImageView(m_Device, &viewInfo, NULL, &stagingImageView); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - viewInfo.image = targetImage; viewInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, sub.mip, 1, sub.slice, 1}; vkr = m_pDriver->vkCreateImageView(m_Device, &viewInfo, NULL, &targetImageView); RDCASSERTEQUAL(vkr, VK_SUCCESS); - viewInfo.image = stencilStagingImage; + viewInfo.format = VK_FORMAT_R32G32B32A32_UINT; + viewInfo.image = colorImage; + viewInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + vkr = m_pDriver->vkCreateImageView(m_Device, &viewInfo, NULL, &colorImageAliasView); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + + viewInfo.image = dsImage; viewInfo.format = VK_FORMAT_D32_SFLOAT_S8_UINT; - viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1}; + vkr = m_pDriver->vkCreateImageView(m_Device, &viewInfo, NULL, &depthOnlyImageView); - vkr = m_pDriver->vkBindImageMemory(m_Device, stencilStagingImage, gpuMem, - stencilStagingImageOffset); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - vkr = m_pDriver->vkCreateImageView(m_Device, &viewInfo, NULL, &stencilStagingImageView); - RDCASSERTEQUAL(vkr, VK_SUCCESS); + viewInfo.subresourceRange = {VK_IMAGE_ASPECT_STENCIL_BIT, 0, 1, 0, 1}; + vkr = m_pDriver->vkCreateImageView(m_Device, &viewInfo, NULL, &stencilOnlyImageView); } VkBufferCreateInfo bufferInfo = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO}; bufferInfo.size = AlignUp((uint32_t)(numEvents * sizeof(EventInfo)), 4096U); - bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; + bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; vkr = m_pDriver->vkCreateBuffer(m_Device, &bufferInfo, NULL, &dstBuffer); RDCASSERTEQUAL(vkr, VK_SUCCESS); @@ -2643,41 +2677,68 @@ bool VulkanDebugManager::PixelHistorySetupResources(PixelHistoryResources &resou resources.colorImage = colorImage; resources.colorImageView = colorImageView; - resources.stencilImage = stencilImage; - resources.stencilImageView = stencilImageView; - resources.stagingImage = stagingImage; - resources.stagingImageView = stagingImageView; - resources.stencilStagingImage = stencilStagingImage; - resources.stencilStagingImageView = stencilStagingImageView; - resources.targetImageView = targetImageView; + resources.dsImage = dsImage; + resources.dsImageView = dsImageView; resources.gpuMem = gpuMem; + resources.colorImageAliasView = colorImageAliasView; + resources.targetImageView = targetImageView; + resources.depthOnlyImageView = depthOnlyImageView; + resources.stencilOnlyImageView = stencilOnlyImageView; + resources.bufferMemory = bufferMemory; resources.dstBuffer = dstBuffer; return true; } -void VulkanReplay::UpdatePixelHistoryDescriptor(VkImageView sourceView, VkImageView destView) +void VulkanReplay::UpdatePixelHistoryDescriptor(VkImageView sourceView, VkImageView depthImageView, + VkImageView stencilImageView, VkBuffer destBuffer) { - VkDescriptorImageInfo srcdesc = {0}; - srcdesc.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - srcdesc.imageView = Unwrap(sourceView); - srcdesc.sampler = Unwrap(m_General.PointSampler); // not used - we use texelFetch + VkDescriptorBufferInfo destdesc = {0}; + destdesc.buffer = Unwrap(destBuffer); + destdesc.range = VK_WHOLE_SIZE; - VkDescriptorImageInfo destdesc = {0}; - destdesc.imageLayout = VK_IMAGE_LAYOUT_GENERAL; - destdesc.imageView = Unwrap(destView); + { + VkDescriptorImageInfo srcdesc = {}; + srcdesc.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + srcdesc.imageView = Unwrap(sourceView); + srcdesc.sampler = Unwrap(m_General.PointSampler); // not used - we use texelFetch - VkWriteDescriptorSet writeSet[] = { - {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_PixelHistory.MSCopyDescSet), 0, 0, 1, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &srcdesc, NULL, NULL}, - {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_PixelHistory.MSCopyDescSet), 2, 0, 1, - VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &destdesc, NULL, NULL}, - }; + VkWriteDescriptorSet writeSet[] = { + {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_PixelHistory.MSCopyDescSet), 0, 0, + 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &srcdesc, NULL, NULL}, + {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_PixelHistory.MSCopyDescSet), 1, 0, + 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &srcdesc, NULL, NULL}, + {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_PixelHistory.MSCopyDescSet), 2, 0, + 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, NULL, &destdesc, NULL}, + }; - ObjDisp(m_pDriver->GetDev()) - ->UpdateDescriptorSets(Unwrap(m_pDriver->GetDev()), ARRAY_COUNT(writeSet), writeSet, 0, NULL); + ObjDisp(m_pDriver->GetDev()) + ->UpdateDescriptorSets(Unwrap(m_pDriver->GetDev()), ARRAY_COUNT(writeSet), writeSet, 0, NULL); + } + + { + VkDescriptorImageInfo srcdesc[2] = {}; + srcdesc[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + srcdesc[0].imageView = Unwrap(depthImageView); + srcdesc[0].sampler = Unwrap(m_General.PointSampler); // not used - we use texelFetch + + srcdesc[1] = srcdesc[0]; + srcdesc[1].imageView = Unwrap(stencilImageView); + + VkWriteDescriptorSet writeSet[] = { + {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_PixelHistory.MSDepthCopyDescSet), 0, + 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &srcdesc[0], NULL, NULL}, + {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_PixelHistory.MSDepthCopyDescSet), 1, + 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &srcdesc[1], NULL, NULL}, + {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_PixelHistory.MSDepthCopyDescSet), 2, + 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, NULL, &destdesc, NULL}, + }; + + ObjDisp(m_pDriver->GetDev()) + ->UpdateDescriptorSets(Unwrap(m_pDriver->GetDev()), ARRAY_COUNT(writeSet), writeSet, 0, NULL); + } } bool VulkanDebugManager::PixelHistoryDestroyResources(const PixelHistoryResources &r) @@ -2689,18 +2750,18 @@ bool VulkanDebugManager::PixelHistoryDestroyResources(const PixelHistoryResource m_pDriver->vkDestroyImage(dev, r.colorImage, NULL); if(r.colorImageView != VK_NULL_HANDLE) m_pDriver->vkDestroyImageView(dev, r.colorImageView, NULL); - if(r.stencilImage != VK_NULL_HANDLE) - m_pDriver->vkDestroyImage(dev, r.stencilImage, NULL); - if(r.stencilImageView != VK_NULL_HANDLE) - m_pDriver->vkDestroyImageView(dev, r.stencilImageView, NULL); - if(r.stagingImage != VK_NULL_HANDLE) - m_pDriver->vkDestroyImage(dev, r.stagingImage, NULL); - if(r.stagingImageView != VK_NULL_HANDLE) - m_pDriver->vkDestroyImageView(dev, r.stagingImageView, NULL); - if(r.stencilStagingImage != VK_NULL_HANDLE) - m_pDriver->vkDestroyImage(dev, r.stencilStagingImage, NULL); - if(r.stencilStagingImageView != VK_NULL_HANDLE) - m_pDriver->vkDestroyImageView(dev, r.stencilStagingImageView, NULL); + if(r.dsImage != VK_NULL_HANDLE) + m_pDriver->vkDestroyImage(dev, r.dsImage, NULL); + if(r.dsImageView != VK_NULL_HANDLE) + m_pDriver->vkDestroyImageView(dev, r.dsImageView, NULL); + if(r.colorImageAliasView != VK_NULL_HANDLE) + m_pDriver->vkDestroyImageView(dev, r.colorImageAliasView, NULL); + if(r.targetImageView != VK_NULL_HANDLE) + m_pDriver->vkDestroyImageView(dev, r.targetImageView, NULL); + if(r.depthOnlyImageView != VK_NULL_HANDLE) + m_pDriver->vkDestroyImageView(dev, r.depthOnlyImageView, NULL); + if(r.stencilOnlyImageView != VK_NULL_HANDLE) + m_pDriver->vkDestroyImageView(dev, r.stencilOnlyImageView, NULL); if(r.dstBuffer != VK_NULL_HANDLE) m_pDriver->vkDestroyBuffer(dev, r.dstBuffer, NULL); if(r.bufferMemory != VK_NULL_HANDLE) @@ -2876,7 +2937,8 @@ rdcarray VulkanReplay::PixelHistory(rdcarray even imginfo.format, imginfo.samples, sub, (uint32_t)events.size()); if(multisampled) - UpdatePixelHistoryDescriptor(resources.targetImageView, resources.stagingImageView); + UpdatePixelHistoryDescriptor(resources.targetImageView, resources.depthOnlyImageView, + resources.stencilOnlyImageView, resources.dstBuffer); PixelHistoryShaderCache *shaderCache = new PixelHistoryShaderCache(m_pDriver); @@ -2893,11 +2955,9 @@ rdcarray VulkanReplay::PixelHistory(rdcarray even callbackInfo.sampleMask = sampleMask; callbackInfo.subImage = resources.colorImage; callbackInfo.subImageView = resources.colorImageView; - callbackInfo.stencilImage = resources.stencilImage; - callbackInfo.stencilImageView = resources.stencilImageView; + callbackInfo.dsImage = resources.dsImage; + callbackInfo.dsImageView = resources.dsImageView; callbackInfo.dstBuffer = resources.dstBuffer; - callbackInfo.stagingImage = resources.stagingImage; - callbackInfo.stencilStagingImage = resources.stencilStagingImage; VulkanOcclusionCallback occlCb(m_pDriver, shaderCache, callbackInfo, occlusionPool, events); m_pDriver->ReplayLog(0, events.back().eventId, eReplay_Full); @@ -2916,7 +2976,7 @@ rdcarray VulkanReplay::PixelHistory(rdcarray even if(events[ev].view != ResourceId()) { - // TODO + // TODO: Check that the slice and mip matches. } if(directWrite || clear) @@ -2998,6 +3058,7 @@ rdcarray VulkanReplay::PixelHistory(rdcarray even RDCASSERTEQUAL(vkr, VK_SUCCESS); std::map eventsWithFrags; + std::map eventPremods; ResourceFormat fmt = MakeResourceFormat(imginfo.format); for(size_t h = 0; h < history.size();) @@ -3019,8 +3080,8 @@ rdcarray VulkanReplay::PixelHistory(rdcarray even mod.postMod.depth = ei.postmod.depth.fdepth; mod.postMod.stencil = ei.postmod.stencil; - int32_t frags = int32_t(ei.dsWithoutShaderDiscard[0]); - int32_t fragsClipped = int32_t(ei.dsWithShaderDiscard[0]); + int32_t frags = int32_t(ei.dsWithoutShaderDiscard[4]); + int32_t fragsClipped = int32_t(ei.dsWithShaderDiscard[4]); mod.shaderOut.col.intValue[0] = frags; mod.shaderOut.col.intValue[1] = fragsClipped; bool someFragsClipped = (fragsClipped < frags); @@ -3028,7 +3089,10 @@ rdcarray VulkanReplay::PixelHistory(rdcarray even // Draws in secondary command buffers will fail this check, // so nothing else needs to be checked in the callback itself. if(frags > 0) + { eventsWithFrags[mod.eventId] = frags; + eventPremods[mod.eventId] = mod.preMod; + } for(int32_t f = 1; f < frags; f++) { @@ -3040,7 +3104,7 @@ rdcarray VulkanReplay::PixelHistory(rdcarray even RDCDEBUG( "PixelHistory event id: %u, fixed shader stencilValue = %u, original shader stencilValue = " "%u", - mod.eventId, ei.dsWithoutShaderDiscard[0], ei.dsWithShaderDiscard[0]); + mod.eventId, ei.dsWithoutShaderDiscard[4], ei.dsWithShaderDiscard[4]); } m_pDriver->vkUnmapMemory(dev, resources.bufferMemory); @@ -3048,8 +3112,11 @@ rdcarray VulkanReplay::PixelHistory(rdcarray even { // Replay to get shader output value, post modification value and primitive ID for every // fragment. + if(multisampled) + UpdatePixelHistoryDescriptor(resources.colorImageAliasView, resources.depthOnlyImageView, + resources.stencilOnlyImageView, resources.dstBuffer); VulkanPixelHistoryPerFragmentCallback perFragmentCB(m_pDriver, shaderCache, callbackInfo, - eventsWithFrags); + eventsWithFrags, eventPremods); m_pDriver->ReplayLog(0, eventsWithFrags.rbegin()->first, eReplay_Full); m_pDriver->SubmitCmds(); m_pDriver->FlushQ(); @@ -3099,7 +3166,6 @@ rdcarray VulkanReplay::PixelHistory(rdcarray even } uint32_t discardOffset = 0; - ResourceFormat resourceFmt = MakeResourceFormat(imginfo.format); ResourceFormat shaderOutFormat = MakeResourceFormat(VK_FORMAT_R32G32B32A32_SFLOAT); for(size_t h = 0; h < history.size(); h++) { @@ -3125,7 +3191,7 @@ rdcarray VulkanReplay::PixelHistory(rdcarray even if((h < history.size() - 1) && (history[h].eventId == history[h + 1].eventId)) { // Get post-modification value if this is not the last fragment for the event. - FillInColor(resourceFmt, bp[offset].postMod, history[h].postMod); + FillInColor(shaderOutFormat, bp[offset].postMod, history[h].postMod); history[h].postMod.depth = bp[offset].postMod.depth.fdepth; } } diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index cd004cfb6..f71d1b724 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -2795,11 +2795,32 @@ rdcarray VulkanReplay::GetUsage(ResourceId id) return m_pDriver->GetUsage(id); } -void VulkanReplay::CopyPixelForPixelHistory(VkCommandBuffer cmd, VkExtent3D extent, uint32_t sample, - VkFormat fmt) +void VulkanReplay::CopyPixelForPixelHistory(VkCommandBuffer cmd, VkOffset2D offset, uint32_t sample, + uint32_t bufferOffset, bool depthCopy) { - m_pDriver->GetDebugManager()->CopyTex2DMSPixel(cmd, m_PixelHistory.MSCopyDescSet, extent, sample, - fmt); + if(m_PixelHistory.MSCopyPipe == VK_NULL_HANDLE) + return; + + VkDescriptorSet descSet; + if(depthCopy) + descSet = m_PixelHistory.MSDepthCopyDescSet; + else + descSet = m_PixelHistory.MSCopyDescSet; + if(!m_pDriver->GetDeviceFeatures().shaderStorageImageWriteWithoutFormat) + return; + + ObjDisp(cmd)->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_COMPUTE, + Unwrap(m_PixelHistory.MSCopyPipe)); + + uint32_t params[8] = {depthCopy, sample, (uint32_t)offset.x, (uint32_t)offset.y, bufferOffset, 0, + 0, 0}; + ObjDisp(cmd)->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_COMPUTE, + Unwrap(m_PixelHistory.MSCopyPipeLayout), 0, 1, + UnwrapPtr(descSet), 0, NULL); + + ObjDisp(cmd)->CmdPushConstants(Unwrap(cmd), Unwrap(m_PixelHistory.MSCopyPipeLayout), + VK_SHADER_STAGE_ALL, 0, 8 * 4, params); + ObjDisp(cmd)->CmdDispatch(Unwrap(cmd), 1, 1, 1); } void VulkanReplay::GetTextureData(ResourceId tex, const Subresource &sub, diff --git a/renderdoc/driver/vulkan/vk_replay.h b/renderdoc/driver/vulkan/vk_replay.h index e09124a47..b2d7015d0 100644 --- a/renderdoc/driver/vulkan/vk_replay.h +++ b/renderdoc/driver/vulkan/vk_replay.h @@ -322,7 +322,8 @@ public: float *maxval); bool GetHistogram(ResourceId texid, const Subresource &sub, CompType typeCast, float minval, float maxval, bool channels[4], rdcarray &histogram); - void UpdatePixelHistoryDescriptor(VkImageView sourceView, VkImageView destView); + void UpdatePixelHistoryDescriptor(VkImageView sourceView, VkImageView depthImageView, + VkImageView stencilImageView, VkBuffer destBuffer); void InitPostVSBuffers(uint32_t eventId); void InitPostVSBuffers(uint32_t eventId, VulkanRenderState &state); @@ -414,8 +415,8 @@ public: void SetDriverInformation(const VkPhysicalDeviceProperties &props); AMDCounters *GetAMDCounters() { return m_pAMDCounters; } - void CopyPixelForPixelHistory(VkCommandBuffer cmd, VkExtent3D extent, uint32_t sample, - VkFormat fmt); + void CopyPixelForPixelHistory(VkCommandBuffer cmd, VkOffset2D offset, uint32_t sample, + uint32_t bufferOffset, bool depthCopy); private: void FetchShaderFeedback(uint32_t eventId); @@ -674,6 +675,9 @@ private: VkDescriptorSetLayout MSCopyDescSetLayout = VK_NULL_HANDLE; VkDescriptorSet MSCopyDescSet = VK_NULL_HANDLE; + VkDescriptorSet MSDepthCopyDescSet = VK_NULL_HANDLE; + VkPipeline MSCopyPipe = VK_NULL_HANDLE; + VkPipelineLayout MSCopyPipeLayout = VK_NULL_HANDLE; } m_PixelHistory; struct HistogramMinMax diff --git a/renderdoc/driver/vulkan/vk_shader_cache.cpp b/renderdoc/driver/vulkan/vk_shader_cache.cpp index bbdfe28fc..ac3fef9f5 100644 --- a/renderdoc/driver/vulkan/vk_shader_cache.cpp +++ b/renderdoc/driver/vulkan/vk_shader_cache.cpp @@ -91,6 +91,8 @@ static const BuiltinShaderConfig builtinShaders[] = { rdcspv::ShaderStage::Fragment, FeatureCheck::NoCheck, true}, {BuiltinShader::TexRemapSInt, EmbeddedResource(glsl_texremap_frag), rdcspv::ShaderStage::Fragment, FeatureCheck::NoCheck, true}, + {BuiltinShader::PixelHistoryMSCopyCS, EmbeddedResource(glsl_pixelhistory_mscopy_comp), + rdcspv::ShaderStage::Compute, FeatureCheck::NoCheck, true}, {BuiltinShader::PixelHistoryPrimIDFS, EmbeddedResource(glsl_pixelhistory_primid_frag), rdcspv::ShaderStage::Fragment, FeatureCheck::NoCheck, true}, {BuiltinShader::ShaderDebugSampleVS, EmbeddedResource(glsl_shaderdebug_sample_vert), diff --git a/renderdoc/driver/vulkan/vk_shader_cache.h b/renderdoc/driver/vulkan/vk_shader_cache.h index 35ade9750..d6d0849e4 100644 --- a/renderdoc/driver/vulkan/vk_shader_cache.h +++ b/renderdoc/driver/vulkan/vk_shader_cache.h @@ -54,6 +54,7 @@ enum class BuiltinShader TexRemapFloat, TexRemapUInt, TexRemapSInt, + PixelHistoryMSCopyCS, PixelHistoryPrimIDFS, ShaderDebugSampleVS, Count, diff --git a/renderdoc/renderdoc.vcxproj b/renderdoc/renderdoc.vcxproj index f45acf989..8382b81fd 100644 --- a/renderdoc/renderdoc.vcxproj +++ b/renderdoc/renderdoc.vcxproj @@ -599,6 +599,7 @@ + diff --git a/util/test/demos/vk/vk_pixel_history_test.cpp b/util/test/demos/vk/vk_pixel_history_test.cpp index 979bf33a4..c47526cf5 100644 --- a/util/test/demos/vk/vk_pixel_history_test.cpp +++ b/util/test/demos/vk/vk_pixel_history_test.cpp @@ -81,6 +81,12 @@ layout(location = 0, index = 0) out vec4 Color; void main() { + if(gl_PrimitiveID == 0) + { + Color = vec4(1, 0, 1, 1); + return; + } + if (gl_SampleID == 0) Color = vec4(1, 0, 0, 1); else if (gl_SampleID == 1) diff --git a/util/test/tests/Vulkan/VK_Pixel_History.py b/util/test/tests/Vulkan/VK_Pixel_History.py index 84db3e3e2..164b72bf2 100644 --- a/util/test/tests/Vulkan/VK_Pixel_History.py +++ b/util/test/tests/Vulkan/VK_Pixel_History.py @@ -117,13 +117,14 @@ class VK_Pixel_History(rdtest.TestCase): beg_renderpass_eid = self.find_draw("Multisampled: begin renderpass").next.eventId - x, y = 100, 200 + x, y = 140, 130 sub.sample = 1 rdtest.log.print("Testing pixel {}, {} at sample {}".format(x, y, sub.sample)) modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.typeCast) events = [ [[event_id, beg_renderpass_eid], [passed, True], [post_mod_col, (0.0, 1.0, 0.0, 1.0)]], - [[event_id, draw_eid], [passed, True], [post_mod_col, (0.0, 0.0, 1.0, 1.0)]], + [[event_id, draw_eid], [passed, True], [primitive_id, 0], [shader_out_col, (1.0, 0.0, 1.0, 1.0)], [post_mod_col, (1.0, 0.0, 1.0, 1.0)]], + [[event_id, draw_eid], [passed, True], [primitive_id, 1], [shader_out_col, (0.0, 0.0, 1.0, 1.0)], [post_mod_col, (0.0, 0.0, 1.0, 1.0)]], ] self.check_events(events, modifs, True) self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.typeCast) @@ -133,7 +134,8 @@ class VK_Pixel_History(rdtest.TestCase): modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.typeCast) events = [ [[event_id, beg_renderpass_eid], [passed, True], [post_mod_col, (0.0, 1.0, 0.0, 1.0)]], - [[event_id, draw_eid], [passed, True], [post_mod_col, (0.0, 1.0, 1.0, 1.0)]], + [[event_id, draw_eid], [passed, True], [primitive_id, 0], [shader_out_col, (1.0, 0.0, 1.0, 1.0)], [post_mod_col, (1.0, 0.0, 1.0, 1.0)]], + [[event_id, draw_eid], [passed, True], [primitive_id, 1], [shader_out_col, (0.0, 1.0, 1.0, 1.0)], [post_mod_col, (0.0, 1.0, 1.0, 1.0)]], ] self.check_events(events, modifs, True) self.check_pixel_value(tex, x, y, value_selector(modifs[-1].postMod.col), sub=sub, cast=rt.typeCast)