https://github.com/wavestoweather/PCViewer.git
Tip revision: e4a2a107c7790a4948783b0f8515ddf84afafc15 authored by Josef Stumpfegger on 24 November 2022, 12:56:34 UTC
Added links to the replicability video and dataset.
Added links to the replicability video and dataset.
Tip revision: e4a2a10
PCRenderer.cpp
#define NOSTATICS
#include "PCRenderer.hpp"
#undef NOSTATICS
#include "PCUtil.h"
PCRenderer::PCRenderer(const VkUtil::Context& context, uint32_t width, uint32_t height, VkDescriptorSetLayout uniformLayout, VkDescriptorSetLayout dataLayout):
_pipelineInstance(PipelineSingleton::getInstance(context, {width, height, uniformLayout, dataLayout}))
{
//creating the render resources
VkFormat intermediateFormat = VK_FORMAT_R32_SFLOAT;
VkFormat plotFormat = VK_FORMAT_R8G8B8A8_UNORM;
VkUtil::createImage(context.device, width, height, intermediateFormat, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, &_intermediateImage);
VkUtil::createImage(context.device, width, height, plotFormat, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, &_plotImage);
VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
VkUtil::addImageToAllocInfo(context.device, _intermediateImage, allocInfo);
uint32_t plotImageOffset = allocInfo.allocationSize;
VkUtil::addImageToAllocInfo(context.device, _plotImage, allocInfo);
allocInfo.memoryTypeIndex = VkUtil::findMemoryType(context.physicalDevice, allocInfo.memoryTypeIndex, 0);
VkResult res = vkAllocateMemory(context.device, &allocInfo, nullptr, &_imageMemory); check_vk_result(res);
res = vkBindImageMemory(context.device, _intermediateImage, _imageMemory, 0);
res = vkBindImageMemory(context.device, _plotImage, _imageMemory, plotImageOffset);
VkUtil::createImageView(context.device, _intermediateImage, intermediateFormat, 1, VK_IMAGE_ASPECT_COLOR_BIT, &_intermediateView);
VkUtil::createImageView(context.device, _plotImage, plotFormat, 1, VK_IMAGE_ASPECT_COLOR_BIT, &_plotView);
std::vector<VkImageView> attachments{_intermediateView};
VkUtil::createFrameBuffer(context.device, _pipelineInstance.renderPass, attachments, width, height, &_framebuffer);
VkUtil::transitionImageLayoutDirect(context.device, context.commandPool, context.queue, {_intermediateImage, _plotImage}, {intermediateFormat, plotFormat}, {VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_UNDEFINED}, {VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL});
//VkUtil::createDescriptorSets(context.device, {_pipelineInstance.pipelineInfo.descriptorSetLayout}, context.descriptorPool, &_intermediateSet);
//VkUtil::updateStorageImageDescriptorSet(context.device, _intermediateView, VK_IMAGE_LAYOUT_GENERAL, 0, _intermediateSet);
VkUtil::createDescriptorSets(context.device, {_pipelineInstance.computeInfo.descriptorSetLayout}, context.descriptorPool, &_computeSet);
VkUtil::updateStorageImageDescriptorSet(context.device, _intermediateView, VK_IMAGE_LAYOUT_GENERAL, 0, _computeSet);
VkUtil::updateStorageImageDescriptorSet(context.device, _plotView, VK_IMAGE_LAYOUT_GENERAL, 1, _computeSet);
}
PCRenderer::~PCRenderer(){
PipelineSingleton::notifyInstanceShutdown(_pipelineInstance);
auto device = _pipelineInstance.context.device;
if(_framebuffer) vkDestroyFramebuffer(device, _framebuffer, nullptr);
if(_intermediateImage) vkDestroyImage(device, _intermediateImage, nullptr);
if(_plotImage) vkDestroyImage(device, _plotImage, nullptr);
if(_intermediateView) vkDestroyImageView(device, _intermediateView, nullptr);
if(_plotView) vkDestroyImageView(device, _plotView, nullptr);
if(_imageMemory) vkFreeMemory(device, _imageMemory, nullptr);
if(_intermediateSet) vkFreeDescriptorSets(device, _pipelineInstance.context.descriptorPool, 1, &_intermediateSet);
if(_computeSet) vkFreeDescriptorSets(device, _pipelineInstance.context.descriptorPool, 1, &_computeSet);
}
void PCRenderer::renderPCPlots(std::list<DrawList>& drawlists, const GlobalPCSettings& globalSettings){
PCUtil::Stopwatch stopwatch(std::cout, "PCRenderer::renderPCPlots(...)");
// renaming the all things so that they can be accessed in the later code more efficiently and without having to rewrite most of the code
auto& attributes = globalSettings.attributes;
bool* attributeEnabled = globalSettings.attributeEnabled;
std::vector<int>& attributeOrder = globalSettings.attributeOrder;
auto device = _pipelineInstance.context.device;
auto placeOfInd = [&](int ind, bool countDisabled = false){
int place = 0;
for (int i : attributeOrder) {
if (i == ind)
break;
if (attributeEnabled[i] || countDisabled)
place++;
}
return place;
};
// end renaming ----------------------------------------------------------------
VkResult err;
err = vkQueueWaitIdle(_pipelineInstance.context.queue);
check_vk_result(err);
//beginning the command buffer
VkCommandBuffer command_buffer;
VkUtil::createCommandBuffer(_pipelineInstance.context.device, _pipelineInstance.context.commandPool, &command_buffer);
//now using the memory barrier to transition image state
VkImageMemoryBarrier use_barrier[1] = {};
use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
use_barrier[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
use_barrier[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
use_barrier[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
use_barrier[0].newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
use_barrier[0].image = _intermediateImage;
use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
use_barrier[0].subresourceRange.levelCount = 1;
use_barrier[0].subresourceRange.layerCount = 1;
//vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, NULL, 0, NULL, 1, use_barrier);
//drawing via copying the indeces into the index buffer
//the indeces have to have just the right ordering for the vertices
int amtOfIndeces = 0;
for (int i = 0; i < attributes.size(); i++) {
if (attributeEnabled[i])
amtOfIndeces++;
}
//filling the uniform buffer and copying it into the end of the uniformbuffer
UniformBufferObject ubo = {};
ubo.amtOfVerts = amtOfIndeces;
ubo.amtOfAttributes = attributes.size();
ubo.color = { 1,1,1,1 };
ubo.vertTransformations.resize(ubo.amtOfAttributes);
ubo.padding = 0;
int c = 0;
for (int i : attributeOrder) {
ubo.vertTransformations[i].x = c;
if (attributeEnabled[i])
c++;
ubo.vertTransformations[i].y = attributes[i].min;
ubo.vertTransformations[i].z = attributes[i].max;
}
std::vector<std::pair<int, int>> order;
for (int i = 0; i < attributes.size(); i++) {
if (attributeEnabled[i]) {
order.push_back(std::pair<int, int>(i, placeOfInd(i)));
}
}
std::sort(order.begin(), order.end(), [](std::pair<int, int>a, std::pair<int, int>b) {return a.second < b.second; });
//copying the uniform buffer
void* da;
c = 0;
for (DrawList& ds : drawlists) {
uint32_t uboSize = sizeof(UniformBufferObject) - sizeof(UniformBufferObject::vertTransformations);
uint32_t trafoSize = sizeof(ubo.vertTransformations[0]) * ubo.vertTransformations.size();
std::vector<uint8_t> bits(uboSize + trafoSize);
ubo.vertTransformations[0].w = 0;
ubo.color = ds.color;
std::copy_n(reinterpret_cast<uint8_t*>(&ubo), uboSize, bits.data());
std::copy_n(reinterpret_cast<uint8_t*>(ubo.vertTransformations.data()), trafoSize, bits.data() + uboSize);
vkMapMemory(device, ds.dlMem, 0, bits.size(), 0, &da);
memcpy(da, bits.data(), bits.size());
vkUnmapMemory(device, ds.dlMem);
ubo.vertTransformations[0].w = 0;
ubo.color = ds.medianColor;
std::copy_n(reinterpret_cast<uint8_t*>(&ubo), uboSize, bits.data());
std::copy_n(reinterpret_cast<uint8_t*>(ubo.vertTransformations.data()), trafoSize, bits.data() + uboSize);
vkMapMemory(device, ds.dlMem, ds.medianUboOffset, bits.size(), 0, &da);
memcpy(da, bits.data(), bits.size());
vkUnmapMemory(device, ds.dlMem);
c++;
}
//vector of command buffers for batch rendering
std::vector<VkCommandBuffer> line_batch_commands;
int max_amt_of_lines = 0;
for (auto drawList = drawlists.begin(); drawList != drawlists.end(); ++drawList) {
if(drawList->show)
max_amt_of_lines += drawList->indices.size();
}
int curIndex = 0;
//counting the amount of active drawLists for histogramm rendering
int activeDrawLists = 0;
VkRenderPassBeginInfo passInfo{};
passInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
passInfo.renderPass = _pipelineInstance.renderPass;
passInfo.framebuffer = _framebuffer;
uint32_t width = _pipelineInstance.context.screenSize[0], height = _pipelineInstance.context.screenSize[1];
passInfo.renderArea = {0, 0, width, height};
passInfo.clearValueCount = 1;
VkClearValue passClear{};
passInfo.pClearValues = &passClear;
//clearing the final color image
VkClearColorValue clear{};
VkImageSubresourceRange range{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
vkCmdClearColorImage(command_buffer, _plotImage, VK_IMAGE_LAYOUT_GENERAL, &clear, 1, &range);
for (auto drawList = drawlists.rbegin(); drawlists.rend() != drawList; ++drawList) {
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, 0, {}, 0, {}, 0, {});
vkCmdBeginRenderPass(command_buffer, &passInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, _pipelineInstance.pipelineInfo.pipeline);
if (!drawList->show)
continue;
VkDeviceSize offsets[] = { 0 };
//vkCmdBindVertexBuffers(g_PcPlotCommandBuffer, 0, 1, &drawList->buffer, offsets);
vkCmdBindIndexBuffer(command_buffer, drawList->indexBuffer, 0, VK_INDEX_TYPE_UINT32);
//binding the right ubo
VkDescriptorSet descSets[2]{drawList->uboDescSet, drawList->dataDescriptorSet};
if (globalSettings.renderSplines)
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, _pipelineInstance.pipelineInfo.pipelineLayout, 0, 2, descSets, 0, nullptr);
else
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, _pipelineInstance.pipelineInfo.pipelineLayout, 0, 2, descSets, 0, nullptr);
vkCmdSetLineWidth(command_buffer, 1.0f);
//ready to draw with draw indexed
uint32_t amtOfI = drawList->indices.size() * (order.size() + 3);
vkCmdDrawIndexed(command_buffer, amtOfI, 1, 0, 0, 0);
vkCmdEndRenderPass(command_buffer);
VkUtil::transitionImageLayout(command_buffer, _intermediateImage, VK_FORMAT_R32_SFLOAT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL);
//resolving to the final image
descSets[0] = _computeSet;
descSets[1] = drawList->uboDescSet;
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, _pipelineInstance.computeInfo.pipeline);
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, _pipelineInstance.computeInfo.pipelineLayout, 0, 2, descSets, 0, nullptr);
const uint32_t workWidth = 256, workHeight = 1;
vkCmdDispatch(command_buffer, (width + workWidth - 1) / workWidth, (height + workHeight - 1) / workHeight, 1);
#ifdef PRINTRENDERTIME
uint32_t boolSize;
for (DataSet& ds : g_PcPlotDataSets) {
if (ds.name == drawList->parentDataSet) {
boolSize = ds.data.size();
break;
}
}
bool* active = new bool[boolSize];
VkUtil::downloadData(g_Device, drawList->dlMem, drawList->activeIndicesBufferOffset, boolSize * sizeof(bool), active);
for (int i = 0; i < boolSize; ++i) {
if (active[i]) ++amtOfLines;
}
delete[] active;
//amtOfLines += drawList->activeInd.size();
#endif
}
VkUtil::commitCommandBuffer(_pipelineInstance.context.queue, command_buffer);
err = vkQueueWaitIdle(_pipelineInstance.context.queue); check_vk_result(err);
vkFreeCommandBuffers(device, _pipelineInstance.context.commandPool, 1, &command_buffer);
}
PCRenderer::PipelineSingleton::PipelineSingleton(const VkUtil::Context& inContext, const PipelineInput& input){
context = inContext;
context.screenSize[0] = input.width;
context.screenSize[1] = input.height;
//----------------------------------------------------------------------------------------------
//creating the pipeline for spline rendering
//----------------------------------------------------------------------------------------------
VkShaderModule shaderModules[5]{};
auto vertexBytes = PCUtil::readByteFile(_vertexShader);
shaderModules[0] = VkUtil::createShaderModule(context.device, vertexBytes);
auto geometryBytes = PCUtil::readByteFile(_geometryShader);
shaderModules[3] = VkUtil::createShaderModule(context.device, geometryBytes);
auto fragmentBytes = PCUtil::readByteFile(_fragmentShader);
shaderModules[4] = VkUtil::createShaderModule(context.device, fragmentBytes);
VkVertexInputBindingDescription bindingDescription{};
bindingDescription.binding = 0;
bindingDescription.stride = sizeof(float);
bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
VkVertexInputAttributeDescription attributeDescription{};
attributeDescription.binding = 0;
attributeDescription.location = 0;
attributeDescription.format = VK_FORMAT_UNDEFINED;
attributeDescription.offset = 0;
VkPipelineVertexInputStateCreateInfo vertexInfo{};
vertexInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
VkPipelineRasterizationStateCreateInfo rasterizer{};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.depthClampEnable = VK_FALSE;
rasterizer.rasterizerDiscardEnable = VK_FALSE;
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = VK_CULL_MODE_NONE;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizer.depthBiasEnable = VK_FALSE;
rasterizer.depthBiasClamp = 0.0f;
rasterizer.depthBiasConstantFactor = 0.0f;
rasterizer.depthBiasSlopeFactor = 0.0f;
VkPipelineMultisampleStateCreateInfo multisampling{};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.sampleShadingEnable = VK_FALSE;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multisampling.minSampleShading = 1.0f;
multisampling.pSampleMask = nullptr;
multisampling.alphaToCoverageEnable = VK_FALSE;
multisampling.alphaToOneEnable = VK_FALSE;
VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT;
colorBlendAttachment.blendEnable = VK_TRUE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
VkPipelineColorBlendStateCreateInfo colorBlending = {};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.logicOp = VK_LOGIC_OP_COPY;
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
colorBlending.blendConstants[0] = 0.0f;
colorBlending.blendConstants[1] = 0.0f;
colorBlending.blendConstants[2] = 0.0f;
colorBlending.blendConstants[3] = 0.0f;
VkUtil::BlendInfo blendInfo;
blendInfo.blendAttachment = colorBlendAttachment;
blendInfo.createInfo = colorBlending;
std::vector<VkDescriptorSetLayoutBinding> bindings;
VkDescriptorSetLayoutBinding uboLayoutBinding{};
uboLayoutBinding.binding = 0;
uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
uboLayoutBinding.descriptorCount = 1;
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings.push_back(uboLayoutBinding);
//VkUtil::createDescriptorSetLayout(context.device, bindings, &pipelineInfo.descriptorSetLayout);
std::vector<VkDescriptorSetLayout> descriptorSetLayouts{input.uniformLayout, input.dataLayout};
std::vector<VkDynamicState> dynamicStateVec{VK_DYNAMIC_STATE_LINE_WIDTH};
VkUtil::createRenderPass(context.device, VkUtil::PASS_TYPE_FLOAT, &renderPass);
VkUtil::createPipeline(context.device, &vertexInfo, input.width, input.height, dynamicStateVec, shaderModules, VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY, &rasterizer, &multisampling, nullptr, &blendInfo, descriptorSetLayouts, &renderPass, &pipelineInfo.pipelineLayout, &pipelineInfo.pipeline);
//----------------------------------------------------------------------------------------------
//creating resolve compute pipeline
//----------------------------------------------------------------------------------------------
auto computeBytes = PCUtil::readByteFile(_computeShader);
auto computeModule = VkUtil::createShaderModule(context.device, computeBytes);
bindings.clear();
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
bindings.push_back(uboLayoutBinding);
uboLayoutBinding.binding = 1;
bindings.push_back(uboLayoutBinding);
VkUtil::createDescriptorSetLayout(context.device, bindings, &computeInfo.descriptorSetLayout);
descriptorSetLayouts = {computeInfo.descriptorSetLayout, input.uniformLayout};
VkUtil::createComputePipeline(context.device, computeModule, descriptorSetLayouts, &computeInfo.pipelineLayout, &computeInfo.pipeline);
}
int PCRenderer::PipelineSingleton::_usageCount = 0;