Files
renderdoc/util/test/demos/vk/vk_pixel_history.cpp
T

875 lines
32 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020-2025 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 "vk_test.h"
RD_TEST(VK_Pixel_History, VulkanGraphicsTest)
{
static constexpr const char *Description = "Tests pixel history";
std::string common = R"EOSHADER(
#version 460 core
)EOSHADER";
const std::string vertex = R"EOSHADER(
layout(location = 0) in vec3 Position;
layout(location = 1) in vec4 Color;
layout(location = 0) INTERP out COL_TYPE outCol;
void main()
{
gl_Position = vec4(Position.xyz, 1);
outCol = ProcessColor(Color);
}
)EOSHADER";
const std::string pixel = R"EOSHADER(
layout(location = 0) INTERP in COL_TYPE inCol;
#if COLOR == 1
layout(location = 0, index = 0) out COL_TYPE Color;
#endif
void main()
{
if (gl_FragCoord.x < DISCARD_X+1 && gl_FragCoord.x > DISCARD_X)
discard;
if (inCol.x > 10000 && inCol.y > 10000 && inCol.z > 10000)
discard;
#if COLOR == 1
Color = inCol + COL_TYPE(0, 0, 0, ProcessColor(vec4(ALPHA_ADD)).x);
#endif
}
)EOSHADER";
std::string mspixel = R"EOSHADER(
#if COLOR == 1
layout(location = 0, index = 0) out COL_TYPE Color;
#endif
void main()
{
#if COLOR == 1
vec4 col;
if (gl_SampleID == 0)
col = vec4(1, 0, 0, 1+ALPHA_ADD);
else if (gl_SampleID == 1)
col = vec4(0, 0, 1, 1+ALPHA_ADD);
else if (gl_SampleID == 2)
col = vec4(0, 1, 1, 1+ALPHA_ADD);
else if (gl_SampleID == 3)
col = vec4(1, 1, 1, 1+ALPHA_ADD);
Color = ProcessColor(col);
#endif
}
)EOSHADER";
std::string comp = R"EOSHADER(
layout(set = 0, binding = 0) writeonly uniform IMAGE_TYPE Output;
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void main()
{
vec4 data = vec4(3, 3, 3, 9);
for(int x=0; x < 10; x++)
for(int y=0; y < 10; y++)
imageStore(Output, ivec3(STORE_X+x, STORE_Y+y, STORE_Z) STORE_SAMPLE, ProcessColor(data));
}
)EOSHADER";
void Prepare(int argc, char **argv)
{
features.depthBounds = true;
features.geometryShader = true;
features.sampleRateShading = true;
features.shaderStorageImageMultisample = true;
features.shaderStorageImageReadWithoutFormat = true;
features.shaderStorageImageWriteWithoutFormat = true;
devExts.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME);
VulkanGraphicsTest::Prepare(argc, argv);
if(!Avail.empty())
return;
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(phys, VK_FORMAT_D32_SFLOAT_S8_UINT, &props);
if((props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) == 0)
{
Avail = "D32S8 not supported";
return;
}
}
struct VKTestBatch
{
std::string name;
bool secondary = false;
bool uint = false;
AllocatedImage colImg;
AllocatedImage depthImg;
VkFormat depthFormat;
VkDescriptorSet compset;
VkPipelineLayout compLayout;
VkPipeline comppipe;
VkFramebuffer fb;
VkRenderPass rp;
VkExtent2D extent;
VkPipeline depthWritePipe;
VkPipeline fixedScissorPassPipe;
VkPipeline fixedScissorFailPipe;
VkPipeline dynamicStencilRefPipe;
VkPipeline dynamicStencilMaskPipe;
VkPipeline depthEqualPipe;
VkPipeline depthPipe;
VkPipeline stencilWritePipe;
VkPipeline backgroundPipe;
VkPipeline noFsPipe;
VkPipeline basicPipe;
VkPipeline colorMaskPipe;
VkPipeline cullFrontPipe;
VkPipeline depthBoundsPipe1;
VkPipeline depthBoundsPipe2;
VkPipeline sampleColourPipe;
};
std::vector<VKTestBatch> batches;
void BuildTestBatch(const std::string &name, VkSampleCountFlagBits sampleCount,
VkFormat colourFormat, VkFormat depthStencilFormat, int targetMip = -1,
int targetSlice = -1, int targetDepthSlice = -1)
{
batches.push_back({});
VKTestBatch &batch = batches.back();
batch.name = name;
batch.extent = mainWindow->scissor.extent;
uint32_t arraySize = targetSlice >= 0 ? 5 : 1;
uint32_t mips = targetMip >= 0 ? 4 : 1;
uint32_t depth = targetDepthSlice >= 0 ? 14 : 0;
uint32_t mip = (uint32_t)std::max(0, targetMip);
uint32_t slice = (uint32_t)std::max(0, targetSlice);
if(depth > 1)
slice = (uint32_t)targetDepthSlice;
VkPipelineLayout layout = createPipelineLayout(vkh::PipelineLayoutCreateInfo());
std::string defines;
defines +=
"#define COLOR " + std::string(colourFormat == VK_FORMAT_UNDEFINED ? "0" : "1") + "\n";
std::string imgprefix;
if(colourFormat == VK_FORMAT_R8G8B8A8_UINT || colourFormat == VK_FORMAT_R16G16B16A16_UINT ||
colourFormat == VK_FORMAT_R32G32B32A32_UINT)
{
batch.uint = true;
imgprefix = "u";
defines += R"(
#define COL_TYPE uvec4
#define ALPHA_ADD 3
#define INTERP flat
uvec4 ProcessColor(vec4 col)
{
uvec4 ret = uvec4(16*col);
if(col.x < 0.0f) ret.x = 0xfffffff0;
if(col.y < 0.0f) ret.y = 0xfffffff0;
if(col.z < 0.0f) ret.z = 0xfffffff0;
return ret;
}
)";
}
else
{
defines += R"(
#define COL_TYPE vec4
#define ALPHA_ADD 1.75
#define INTERP
vec4 ProcessColor(vec4 col)
{
vec4 ret = col;
// this obviously won't overflow F32 but it will overflow anything 16-bit and under
if(col.x < 0.0f) ret.x = 100000.0f;
if(col.y < 0.0f) ret.y = 100000.0f;
if(col.z < 0.0f) ret.z = 100000.0f;
return ret;
}
)";
}
defines += "#define DISCARD_X " + std::to_string(150 >> mip) + "\n";
defines += "#define IMAGE_TYPE " +
std::string(depth > 1 ? imgprefix + "image3D"
: sampleCount != VK_SAMPLE_COUNT_1_BIT ? imgprefix + "image2DMSArray"
: imgprefix + "image2DArray") +
"\n";
defines += "#define STORE_X " + std::to_string(220 >> mip) + "\n";
defines += "#define STORE_Y " + std::to_string(80 >> mip) + "\n";
defines += "#define STORE_Z " + std::to_string(slice) + "\n";
defines += "#define STORE_SAMPLE " +
std::string(sampleCount != VK_SAMPLE_COUNT_1_BIT ? ", 0" : "") + "\n";
defines += "\n\n";
VkPipelineShaderStageCreateInfo vertexShader =
CompileShaderModule(common + defines + vertex, ShaderLang::glsl, ShaderStage::vert, "main");
VkPipelineShaderStageCreateInfo fragmentShader =
CompileShaderModule(common + defines + pixel, ShaderLang::glsl, ShaderStage::frag, "main");
vkh::RenderPassCreator renderPassCreateInfo;
std::vector<VkAttachmentReference> cols;
std::vector<VkImageView> atts;
VkImageView colView = VK_NULL_HANDLE;
if(colourFormat != VK_FORMAT_UNDEFINED)
{
batch.colImg = AllocatedImage(
this,
vkh::ImageCreateInfo(batch.extent.width, batch.extent.height, depth, colourFormat,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
mips, arraySize, sampleCount,
depth > 1 ? VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT : 0),
VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY}));
setName(batch.colImg.image, batch.name + " colImg");
colView = createImageView(vkh::ImageViewCreateInfo(
batch.colImg.image, VK_IMAGE_VIEW_TYPE_2D_ARRAY, colourFormat, {},
vkh::ImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, mip, 1, slice, 1)));
setName(colView, batch.name + " colView");
VkImageView storeView = createImageView(vkh::ImageViewCreateInfo(
batch.colImg.image, depth > 1 ? VK_IMAGE_VIEW_TYPE_3D : VK_IMAGE_VIEW_TYPE_2D_ARRAY,
colourFormat, {},
vkh::ImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, mip, 1, 0,
VK_REMAINING_ARRAY_LAYERS)));
setName(storeView, batch.name + " storeView");
atts.push_back(colView);
renderPassCreateInfo.attachments.push_back(vkh::AttachmentDescription(
colourFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL,
VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE, sampleCount));
cols = {VkAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL})};
VkDescriptorSetLayout compsetlayout =
createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo({
{0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT},
}));
batch.compLayout = createPipelineLayout(vkh::PipelineLayoutCreateInfo({compsetlayout}));
batch.comppipe = createComputePipeline(vkh::ComputePipelineCreateInfo(
batch.compLayout, CompileShaderModule(common + defines + comp, ShaderLang::glsl,
ShaderStage::comp, "main")));
batch.compset = allocateDescriptorSet(compsetlayout);
vkh::updateDescriptorSets(
device,
{
vkh::WriteDescriptorSet(
batch.compset, 0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
{vkh::DescriptorImageInfo(storeView, VK_IMAGE_LAYOUT_GENERAL, VK_NULL_HANDLE)}),
});
}
batch.depthFormat = depthStencilFormat;
VkImageView depthView = VK_NULL_HANDLE;
if(depthStencilFormat != VK_FORMAT_UNDEFINED)
{
// can't create 2D views of 3D with depth images, so we just use a normal 2D array image
if(depth > 1)
{
arraySize = depth;
depth = 0;
}
batch.depthImg = AllocatedImage(
this,
vkh::ImageCreateInfo(batch.extent.width, batch.extent.height, 0, depthStencilFormat,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, mips, arraySize,
sampleCount),
VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY}));
setName(batch.depthImg.image, batch.name + " depthImg");
VkImageAspectFlags depthAspects = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
if(depthStencilFormat == VK_FORMAT_D16_UNORM || depthStencilFormat == VK_FORMAT_D32_SFLOAT)
depthAspects = VK_IMAGE_ASPECT_DEPTH_BIT;
depthView = createImageView(
vkh::ImageViewCreateInfo(batch.depthImg.image, VK_IMAGE_VIEW_TYPE_2D, depthStencilFormat,
{}, vkh::ImageSubresourceRange(depthAspects, mip, 1, slice, 1)));
setName(depthView, batch.name + " depthView");
atts.push_back(depthView);
renderPassCreateInfo.attachments.push_back(vkh::AttachmentDescription(
depthStencilFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL,
VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE, sampleCount,
VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE));
}
renderPassCreateInfo.addSubpass(cols,
depthView == VK_NULL_HANDLE
? VK_ATTACHMENT_UNUSED
: (uint32_t)renderPassCreateInfo.attachments.size() - 1,
VK_IMAGE_LAYOUT_GENERAL);
batch.rp = createRenderPass(renderPassCreateInfo);
batch.extent.width >>= mip;
batch.extent.height >>= mip;
batch.fb = createFramebuffer(vkh::FramebufferCreateInfo(batch.rp, atts, batch.extent));
vkh::GraphicsPipelineCreateInfo pipeCreateInfo;
pipeCreateInfo.renderPass = batch.rp;
pipeCreateInfo.layout = layout;
pipeCreateInfo.vertexInputState.vertexBindingDescriptions = {vkh::vertexBind(0, DefaultA2V)};
pipeCreateInfo.vertexInputState.vertexAttributeDescriptions = {
vkh::vertexAttr(0, 0, DefaultA2V, pos),
vkh::vertexAttr(1, 0, DefaultA2V, col),
vkh::vertexAttr(2, 0, DefaultA2V, uv),
};
pipeCreateInfo.stages = {vertexShader, fragmentShader};
pipeCreateInfo.rasterizationState.depthClampEnable = VK_FALSE;
pipeCreateInfo.rasterizationState.cullMode = VK_CULL_MODE_BACK_BIT;
pipeCreateInfo.multisampleState.rasterizationSamples = sampleCount;
pipeCreateInfo.depthStencilState.depthTestEnable = VK_TRUE;
pipeCreateInfo.depthStencilState.depthWriteEnable = VK_TRUE;
pipeCreateInfo.depthStencilState.stencilTestEnable = VK_FALSE;
pipeCreateInfo.depthStencilState.front.compareOp = VK_COMPARE_OP_ALWAYS;
pipeCreateInfo.depthStencilState.front.passOp = VK_STENCIL_OP_REPLACE;
pipeCreateInfo.depthStencilState.front.reference = 0x55;
pipeCreateInfo.depthStencilState.front.compareMask = 0xff;
pipeCreateInfo.depthStencilState.front.writeMask = 0xff;
pipeCreateInfo.depthStencilState.back = pipeCreateInfo.depthStencilState.front;
pipeCreateInfo.depthStencilState.depthCompareOp = VK_COMPARE_OP_ALWAYS;
batch.depthWritePipe = createGraphicsPipeline(pipeCreateInfo);
setName(batch.depthWritePipe, batch.name + " depthWritePipe");
{
vkh::GraphicsPipelineCreateInfo depthPipeInfo = pipeCreateInfo;
depthPipeInfo.depthStencilState.depthWriteEnable = VK_FALSE;
depthPipeInfo.depthStencilState.depthTestEnable = VK_TRUE;
depthPipeInfo.depthStencilState.depthCompareOp = VK_COMPARE_OP_EQUAL;
batch.depthEqualPipe = createGraphicsPipeline(depthPipeInfo);
setName(batch.depthEqualPipe, batch.name + " depthEqualPipe");
}
{
vkh::GraphicsPipelineCreateInfo dynamicPipe = pipeCreateInfo;
dynamicPipe.depthStencilState.depthWriteEnable = VK_FALSE;
dynamicPipe.depthStencilState.depthTestEnable = VK_FALSE;
dynamicPipe.dynamicState.dynamicStates = {VK_DYNAMIC_STATE_VIEWPORT};
dynamicPipe.viewportState.scissors = {{{95 >> mip, 245 >> mip}, {20U >> mip, 8U >> mip}}};
batch.fixedScissorPassPipe = createGraphicsPipeline(dynamicPipe);
setName(batch.fixedScissorPassPipe, batch.name + " fixedScissorPassPipe");
dynamicPipe.viewportState.scissors = {{{95 >> mip, 245 >> mip}, {8U >> mip, 8U >> mip}}};
batch.fixedScissorFailPipe = createGraphicsPipeline(dynamicPipe);
setName(batch.fixedScissorFailPipe, batch.name + " fixedScissorFailPipe");
dynamicPipe.dynamicState.dynamicStates.push_back(VK_DYNAMIC_STATE_SCISSOR);
dynamicPipe.dynamicState.dynamicStates.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE);
batch.dynamicStencilRefPipe = createGraphicsPipeline(dynamicPipe);
setName(batch.dynamicStencilRefPipe, batch.name + " dynamicStencilRefPipe");
dynamicPipe.dynamicState.dynamicStates.push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK);
dynamicPipe.dynamicState.dynamicStates.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK);
batch.dynamicStencilMaskPipe = createGraphicsPipeline(dynamicPipe);
setName(batch.dynamicStencilMaskPipe, batch.name + " dynamicStencilMaskPipe");
}
pipeCreateInfo.depthStencilState.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
{
vkh::GraphicsPipelineCreateInfo depthPipeInfo = pipeCreateInfo;
depthPipeInfo.dynamicState.dynamicStates.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS);
depthPipeInfo.depthStencilState.depthBoundsTestEnable = VK_TRUE;
batch.depthPipe = createGraphicsPipeline(depthPipeInfo);
setName(batch.depthPipe, batch.name + " depthPipe");
}
pipeCreateInfo.depthStencilState.stencilTestEnable = VK_TRUE;
batch.stencilWritePipe = createGraphicsPipeline(pipeCreateInfo);
setName(batch.stencilWritePipe, batch.name + " stencilWritePipe");
pipeCreateInfo.depthStencilState.stencilTestEnable = VK_FALSE;
batch.backgroundPipe = createGraphicsPipeline(pipeCreateInfo);
setName(batch.backgroundPipe, batch.name + " backgroundPipe");
pipeCreateInfo.stages = {vertexShader};
pipeCreateInfo.depthStencilState.stencilTestEnable = VK_TRUE;
pipeCreateInfo.depthStencilState.front.reference = 0x33;
batch.noFsPipe = createGraphicsPipeline(pipeCreateInfo);
setName(batch.noFsPipe, batch.name + " noFsPipe");
pipeCreateInfo.stages = {vertexShader, fragmentShader};
pipeCreateInfo.depthStencilState.stencilTestEnable = VK_FALSE;
pipeCreateInfo.depthStencilState.front.reference = 0x55;
pipeCreateInfo.depthStencilState.stencilTestEnable = VK_TRUE;
pipeCreateInfo.depthStencilState.front.compareOp = VK_COMPARE_OP_GREATER;
batch.basicPipe = createGraphicsPipeline(pipeCreateInfo);
setName(batch.basicPipe, batch.name + " basicPipe");
pipeCreateInfo.depthStencilState.stencilTestEnable = VK_FALSE;
{
vkh::GraphicsPipelineCreateInfo maskPipeInfo = pipeCreateInfo;
maskPipeInfo.colorBlendState.attachments[0].colorWriteMask = 0x3;
batch.colorMaskPipe = createGraphicsPipeline(maskPipeInfo);
setName(batch.colorMaskPipe, batch.name + " colorMaskPipe");
}
pipeCreateInfo.rasterizationState.cullMode = VK_CULL_MODE_FRONT_BIT;
batch.cullFrontPipe = createGraphicsPipeline(pipeCreateInfo);
setName(batch.cullFrontPipe, batch.name + " cullFrontPipe");
pipeCreateInfo.rasterizationState.cullMode = VK_CULL_MODE_BACK_BIT;
pipeCreateInfo.depthStencilState.depthBoundsTestEnable = VK_TRUE;
pipeCreateInfo.depthStencilState.minDepthBounds = 0.0f;
pipeCreateInfo.depthStencilState.maxDepthBounds = 1.0f;
batch.depthBoundsPipe1 = createGraphicsPipeline(pipeCreateInfo);
setName(batch.depthBoundsPipe1, batch.name + " depthBoundsPipe1");
pipeCreateInfo.depthStencilState.minDepthBounds = 0.4f;
pipeCreateInfo.depthStencilState.maxDepthBounds = 0.6f;
batch.depthBoundsPipe2 = createGraphicsPipeline(pipeCreateInfo);
setName(batch.depthBoundsPipe2, batch.name + " depthBoundsPipe2");
pipeCreateInfo.depthStencilState.depthBoundsTestEnable = VK_FALSE;
pipeCreateInfo.stages[1] =
CompileShaderModule(common + defines + mspixel, ShaderLang::glsl, ShaderStage::frag, "main");
pipeCreateInfo.depthStencilState.depthWriteEnable = VK_TRUE;
VkSampleMask not3 = 0x7;
if(sampleCount == VK_SAMPLE_COUNT_4_BIT)
pipeCreateInfo.multisampleState.pSampleMask = &not3;
batch.sampleColourPipe = createGraphicsPipeline(pipeCreateInfo);
setName(batch.sampleColourPipe, batch.name + " sampleColourPipe");
}
void RunDraw(VkCommandBuffer cmd, const PixelHistory::draw &draw, uint32_t numInstances = 1)
{
vkCmdDraw(cmd, draw.count, numInstances, draw.first, 0);
}
void RunBatch(const VKTestBatch &b, VkCommandBuffer cmd)
{
float factor = float(b.extent.width) / float(mainWindow->scissor.extent.width);
VkViewport v = {};
v.width = (float)b.extent.width - (20.0f * factor);
v.height = -(float)b.extent.height + (20.0f * factor);
v.x = floorf(10.0f * factor);
v.y = (float)b.extent.height - ceilf(10.0f * factor);
v.maxDepth = 1.0f;
VkRect2D scissor = {{0, 0}, b.extent};
vkCmdSetViewport(cmd, 0, 1, &v);
vkCmdSetScissor(cmd, 0, 1, &scissor);
// draw the setup triangles
pushMarker(cmd, "Setup");
{
setMarker(cmd, "Depth Write");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.depthWritePipe);
RunDraw(cmd, PixelHistory::DepthWrite);
setMarker(cmd, "Depth Equal Setup");
RunDraw(cmd, PixelHistory::DepthEqualSetup);
setMarker(cmd, "Unbound Shader");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.noFsPipe);
RunDraw(cmd, PixelHistory::UnboundPS);
setMarker(cmd, "Stencil Write");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.stencilWritePipe);
RunDraw(cmd, PixelHistory::StencilWrite);
setMarker(cmd, "Background");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.backgroundPipe);
RunDraw(cmd, PixelHistory::Background);
setMarker(cmd, "Cull Front");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.cullFrontPipe);
RunDraw(cmd, PixelHistory::CullFront);
setMarker(cmd, "Depth Bounds Prep");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.depthBoundsPipe1);
RunDraw(cmd, PixelHistory::DepthBoundsPrep);
setMarker(cmd, "Depth Bounds Clip");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.depthBoundsPipe2);
RunDraw(cmd, PixelHistory::DepthBoundsClip);
}
popMarker(cmd);
pushMarker(cmd, "Stress Test");
{
pushMarker(cmd, "Lots of Drawcalls");
{
setMarker(cmd, "300 Draws");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.depthWritePipe);
for(int d = 0; d < 300; ++d)
RunDraw(cmd, PixelHistory::Draws300);
}
popMarker(cmd);
setMarker(cmd, "300 Instances");
RunDraw(cmd, PixelHistory::Instances300, 300);
}
popMarker(cmd);
setMarker(cmd, "Simple Test");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.basicPipe);
RunDraw(cmd, PixelHistory::MainTest);
setMarker(cmd, "Scissor Fail");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.fixedScissorFailPipe);
RunDraw(cmd, PixelHistory::ScissorFail);
setMarker(cmd, "Scissor Pass");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.fixedScissorPassPipe);
RunDraw(cmd, PixelHistory::ScissorPass);
setMarker(cmd, "Stencil Ref");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.dynamicStencilRefPipe);
vkCmdSetScissor(cmd, 0, 1, &scissor);
vkCmdSetStencilReference(cmd, VK_STENCIL_FACE_FRONT_AND_BACK, 0x67);
RunDraw(cmd, PixelHistory::StencilRef);
setMarker(cmd, "Stencil Mask");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.dynamicStencilMaskPipe);
vkCmdSetStencilCompareMask(cmd, VK_STENCIL_FACE_FRONT_AND_BACK, 0);
vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FACE_FRONT_AND_BACK, 0);
RunDraw(cmd, PixelHistory::StencilMask);
setMarker(cmd, "Depth Test");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.depthPipe);
vkCmdSetDepthBounds(cmd, 0.15f, 1.0f);
RunDraw(cmd, PixelHistory::DepthTest);
setMarker(cmd, "Sample Colouring");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.sampleColourPipe);
RunDraw(cmd, PixelHistory::SampleColour);
setMarker(cmd, "Depth Equal Fail");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.depthEqualPipe);
RunDraw(cmd, PixelHistory::DepthEqualFail);
setMarker(cmd, "Depth Equal Pass");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.depthEqualPipe);
if(b.depthFormat == VK_FORMAT_D24_UNORM_S8_UINT)
RunDraw(cmd, PixelHistory::DepthEqualPass24);
else if(b.depthFormat == VK_FORMAT_D16_UNORM_S8_UINT || b.depthFormat == VK_FORMAT_D16_UNORM)
RunDraw(cmd, PixelHistory::DepthEqualPass16);
else
RunDraw(cmd, PixelHistory::DepthEqualPass32);
setMarker(cmd, "Colour Masked");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.colorMaskPipe);
RunDraw(cmd, PixelHistory::ColourMask);
setMarker(cmd, "Overflowing");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.backgroundPipe);
RunDraw(cmd, PixelHistory::OverflowingDraw);
setMarker(cmd, "Per-Fragment discarding");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.backgroundPipe);
RunDraw(cmd, PixelHistory::PerFragDiscard);
setMarker(cmd, "No Output Shader");
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, b.noFsPipe);
RunDraw(cmd, PixelHistory::UnboundPS);
}
int main()
{
// initialise, create window, create context, etc
if(!Init())
return 3;
PixelHistory::init();
AllocatedBuffer vb(
this,
vkh::BufferCreateInfo(sizeof(DefaultA2V) * PixelHistory::vb.size(),
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT),
VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU}));
vb.upload(PixelHistory::vb.data(), PixelHistory::vb.size() * sizeof(DefaultA2V));
VkImage bbBlitSource = VK_NULL_HANDLE;
struct DepthVariant
{
VkFormat fmt;
std::string name;
bool supported;
};
std::vector<DepthVariant> depthFormats = {{VK_FORMAT_D24_UNORM_S8_UINT, "D24S8", false},
{VK_FORMAT_D16_UNORM_S8_UINT, "D16S8", false},
{VK_FORMAT_D32_SFLOAT, "D32", false},
{VK_FORMAT_D16_UNORM, "D16", false}};
for(DepthVariant &fmt : depthFormats)
{
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(phys, fmt.fmt, &props);
if(props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
fmt.supported = true;
}
// work around an NV bug with D32S8 when rendering to 3D textures is happening, prefer D24S8
VkFormat defaultDepthFormat =
depthFormats[0].supported ? VK_FORMAT_D24_UNORM_S8_UINT : VK_FORMAT_D32_SFLOAT_S8_UINT;
BuildTestBatch("Basic", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UNORM, defaultDepthFormat);
bbBlitSource = batches.back().colImg.image;
BuildTestBatch("MSAA", VK_SAMPLE_COUNT_4_BIT, VK_FORMAT_R8G8B8A8_UNORM, defaultDepthFormat);
BuildTestBatch("Colour Only", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UNORM,
VK_FORMAT_UNDEFINED);
BuildTestBatch("Depth Only", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_UNDEFINED, defaultDepthFormat);
BuildTestBatch("Mip & Slice", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UNORM,
defaultDepthFormat, 2, 3);
BuildTestBatch("Slice MSAA", VK_SAMPLE_COUNT_4_BIT, VK_FORMAT_R8G8B8A8_UNORM,
defaultDepthFormat, -1, 3);
BuildTestBatch("Secondary", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UNORM, defaultDepthFormat);
batches.back().secondary = true;
BuildTestBatch("3D texture", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UNORM,
defaultDepthFormat, -1, -1, 8);
for(DepthVariant &fmt : depthFormats)
{
if(fmt.supported)
BuildTestBatch(fmt.name, VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UNORM, fmt.fmt);
}
BuildTestBatch("F16 UNORM", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R16G16B16A16_UNORM,
defaultDepthFormat);
BuildTestBatch("F16 FLOAT", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R16G16B16A16_SFLOAT,
defaultDepthFormat);
BuildTestBatch("F32 FLOAT", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R32G32B32A32_SFLOAT,
defaultDepthFormat);
BuildTestBatch("8-bit uint", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UINT, defaultDepthFormat);
BuildTestBatch("16-bit uint", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R16G16B16A16_UINT,
defaultDepthFormat);
BuildTestBatch("32-bit uint", VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R32G32B32A32_UINT,
defaultDepthFormat);
// for dispatch access, ensure all color images are in GENERAL as render passes for slice tests
// will only transition that one slice
for(const VKTestBatch &b : batches)
{
if(b.colImg.image == VK_NULL_HANDLE)
continue;
VkCommandBuffer cmd = GetCommandBuffer();
vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo());
vkh::cmdPipelineBarrier(
cmd, {vkh::ImageMemoryBarrier(VK_ACCESS_NONE, VK_ACCESS_MEMORY_WRITE_BIT,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL,
b.colImg.image)});
vkEndCommandBuffer(cmd);
Submit(99, 99, {cmd});
}
while(Running())
{
std::vector<VkCommandBuffer> primaries;
std::vector<VkCommandBuffer> secondaries;
for(const VKTestBatch &b : batches)
{
VkCommandBuffer cmd = GetCommandBuffer();
vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo());
vkh::cmdBindVertexBuffers(cmd, 0, {vb.buffer}, {0});
// set clears correctly for depth-only batches, the extra clear value on colour only is ignored
std::vector<VkClearValue> clears;
if(b.colImg.image == VK_NULL_HANDLE)
{
clears = {vkh::ClearValue(1.0f, 0)};
}
else
{
clears = {vkh::ClearValue(0.2f, 0.2f, 0.2f, 1.0f), vkh::ClearValue(1.0f, 0)};
if(b.uint)
{
clears[0].color.uint32[0] = 80;
clears[0].color.uint32[1] = 80;
clears[0].color.uint32[2] = 80;
clears[0].color.uint32[3] = 16;
}
}
if(b.secondary)
{
pushMarker(cmd, "Batch: " + b.name);
{
setMarker(cmd, "Begin RenderPass");
vkCmdBeginRenderPass(cmd,
vkh::RenderPassBeginInfo(b.rp, b.fb, {{0, 0}, b.extent}, clears),
VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
VkCommandBuffer cmd2 = GetCommandBuffer(VK_COMMAND_BUFFER_LEVEL_SECONDARY);
secondaries.push_back(cmd2);
vkBeginCommandBuffer(
cmd2, vkh::CommandBufferBeginInfo(VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT,
vkh::CommandBufferInheritanceInfo(b.rp, 0, b.fb)));
vkh::cmdBindVertexBuffers(cmd2, 0, {vb.buffer}, {0});
RunBatch(b, cmd2);
vkEndCommandBuffer(cmd2);
vkCmdExecuteCommands(cmd, 1, &cmd2);
vkCmdEndRenderPass(cmd);
}
popMarker(cmd);
}
else
{
pushMarker(cmd, "Batch: " + b.name);
{
setMarker(cmd, "Begin RenderPass");
vkCmdBeginRenderPass(cmd,
vkh::RenderPassBeginInfo(b.rp, b.fb, {{0, 0}, b.extent}, clears),
VK_SUBPASS_CONTENTS_INLINE);
RunBatch(b, cmd);
vkCmdEndRenderPass(cmd);
setMarker(cmd, "Compute write");
if(b.comppipe != VK_NULL_HANDLE)
{
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, b.comppipe);
vkh::cmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, b.compLayout, 0,
{b.compset}, {});
vkCmdDispatch(cmd, 1, 1, 1);
}
}
popMarker(cmd);
}
vkEndCommandBuffer(cmd);
primaries.push_back(cmd);
}
{
VkCommandBuffer cmd = GetCommandBuffer();
vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo());
StartUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL);
if(bbBlitSource != VK_NULL_HANDLE)
{
vkh::cmdPipelineBarrier(
cmd, {vkh::ImageMemoryBarrier(
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT,
VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL,
VK_IMAGE_LAYOUT_GENERAL, bbBlitSource)});
blitToSwap(cmd, bbBlitSource, VK_IMAGE_LAYOUT_GENERAL,
mainWindow->GetImage(mainWindow->imgIndex), VK_IMAGE_LAYOUT_GENERAL);
}
FinishUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL);
vkEndCommandBuffer(cmd);
primaries.push_back(cmd);
}
Submit(0, 1, primaries, secondaries);
Present();
}
return 0;
}
};
REGISTER_TEST();