From 81c289ea9cbce19e3406c94f71bee8153e96337c Mon Sep 17 00:00:00 2001 From: ink-soul Date: Fri, 22 Mar 2024 18:05:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0framebuffer=20to=20ppm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ReadMe.md | 10 +++ base/vulkanexamplebase.cpp | 10 +-- base/vulkanexamplebase.h | 1 + src/render/render.cpp | 160 ++++++++++++++++++++++++++++++++++++- src/render/render.h | 21 ++++- 5 files changed, 193 insertions(+), 9 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index dcc37bd..db01147 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -10,6 +10,16 @@ 3. 支持模型的PBR材质渲染 4. 使用基于IBL的环境光照 +#### 分支:add image output ToDo list + +1. 添加ppm格式图片序列输出 +2. 单帧输出,预览效果 +3. 接入ffmpeg将图片序列转图片 + +#### todo list + +1. 参数化模板选择:维护一个模板数组,前端发送一个数组索引 + #### 展示 展示使用录制的GIF并转换为animated webp,存在**画质损失**,仅供参考 diff --git a/base/vulkanexamplebase.cpp b/base/vulkanexamplebase.cpp index fec6985..8cf50b7 100644 --- a/base/vulkanexamplebase.cpp +++ b/base/vulkanexamplebase.cpp @@ -625,13 +625,13 @@ void VulkanExampleBase::initVulkan() for (size_t i = 0; i < args.size(); i++) { if ((args[i] == std::string("-g")) || (args[i] == std::string("--gpu"))) { char* endptr; - uint32_t index = strtol(args[i + 1], &endptr, 10); + selectedPhysicalDeviceIndex = strtol(args[i + 1], &endptr, 10); if (endptr != args[i + 1]) { - if (index > gpuCount - 1) { - std::cerr << "Selected device index " << index << " is out of range, reverting to device 0 (use -listgpus to show available Vulkan devices)" << std::endl; + if (selectedPhysicalDeviceIndex > gpuCount - 1) { + std::cerr << "Selected device index " << selectedPhysicalDeviceIndex << " is out of range, reverting to device 0 (use -listgpus to show available Vulkan devices)" << std::endl; } else { - std::cout << "Selected Vulkan device " << index << std::endl; - selectedDevice = index; + std::cout << "Selected Vulkan device " << selectedPhysicalDeviceIndex << std::endl; + selectedDevice = selectedPhysicalDeviceIndex; } }; break; diff --git a/base/vulkanexamplebase.h b/base/vulkanexamplebase.h index dc46543..772134e 100644 --- a/base/vulkanexamplebase.h +++ b/base/vulkanexamplebase.h @@ -102,6 +102,7 @@ protected: void windowResize(); public: static std::vector args; + uint32_t selectedPhysicalDeviceIndex = 0; bool prepared = false; uint32_t width = 1280; uint32_t height = 720; diff --git a/src/render/render.cpp b/src/render/render.cpp index 0f1d233..d018f87 100644 --- a/src/render/render.cpp +++ b/src/render/render.cpp @@ -1572,6 +1572,153 @@ PlumageRender::PlumageRender() prepared = true; } + void PlumageRender::submitWork(VkCommandBuffer cmdBuffer, VkQueue queue) + { + VkSubmitInfo submitInfo = vks::initializers::submitInfo(); + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &cmdBuffer; + VkFenceCreateInfo fenceInfo = vks::initializers::fenceCreateInfo(); + VkFence fence; + VK_CHECK_RESULT(vkCreateFence(device, &fenceInfo, nullptr, &fence)); + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, fence)); + VK_CHECK_RESULT(vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX)); + vkDestroyFence(device, fence, nullptr); + } + + // todo :根据physicalDeviceIndex确定子文件夹路径,frameIndex确定fileName + void PlumageRender::writeImageToFile(std::string filePath,std::string fileName) + { + char* imageData; + // create dst image to copy + VkImageCreateInfo imageCreateInfo(vks::initializers::imageCreateInfo()); + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; + imageCreateInfo.extent.width = width; + imageCreateInfo.extent.height = height; + imageCreateInfo.extent.depth = 1; + imageCreateInfo.arrayLayers = 1; + imageCreateInfo.mipLevels = 1; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR; + imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; + + VkImage dstImage; + VK_CHECK_RESULT(vkCreateImage(device, &imageCreateInfo, nullptr, &dstImage)); + + // get memory ready to store the image + VkMemoryRequirements memRequirements; + VkMemoryAllocateInfo memAllocInfo(vks::initializers::memoryAllocateInfo()); + VkDeviceMemory dstImageMemory; + vkGetImageMemoryRequirements(device, dstImage, &memRequirements); + memAllocInfo.allocationSize = memRequirements.size; + memAllocInfo.memoryTypeIndex = getMemoryTypeIndex(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + // allocate and bind memory + VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &dstImageMemory)); + VK_CHECK_RESULT(vkBindImageMemory(device, dstImage, dstImageMemory, 0)); + + // blit image and copy to host visualable memory + VkCommandBufferAllocateInfo cmdBufferAllocInfo; + VkCommandBuffer copyCmd; + VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufferAllocInfo, ©Cmd)); + VkCommandBufferBeginInfo cmdBufferBeginInfo(vks::initializers::commandBufferBeginInfo()); + VK_CHECK_RESULT(vkBeginCommandBuffer(copyCmd, &cmdBufferBeginInfo)); + + vks::tools::insertImageMemoryBarrier(copyCmd, dstImage, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT,0,1,0,1 }); + + VkImageCopy imageCopyRegion{}; + imageCopyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageCopyRegion.srcSubresource.layerCount = 1; + imageCopyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageCopyRegion.dstSubresource.layerCount = 1; + imageCopyRegion.extent.width = width; + imageCopyRegion.extent.height = height; + imageCopyRegion.extent.depth = 1; + + vkCmdCopyImage(copyCmd, offscreen.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageCopyRegion); + + // transition dst image to general layout for map memory + vks::tools::insertImageMemoryBarrier(copyCmd, dstImage, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_MEMORY_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT,0,1,0,1}); + + VK_CHECK_RESULT(vkEndCommandBuffer(copyCmd)); + + submitWork(copyCmd, queue); + + // Get layout of the image + VkImageSubresource subResource{}; + subResource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + VkSubresourceLayout subResourceLayout; + + vkGetImageSubresourceLayout(device, dstImage, &subResource, &subResourceLayout); + + // Map image memory + vkMapMemory(device, dstImageMemory, 0, VK_WHOLE_SIZE, 0, (void**)&imageData); + + imageData += subResourceLayout.offset; + + std::ofstream file(fileName, std::ios::out | std::ios::binary); + + // ppm header + file << "P6\n" << width << "\n" << height << "\n" << 255 << "\n"; + + std::vector formatsBGR = { VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SNORM }; + const bool colorSwizzle = (std::find(formatsBGR.begin(), formatsBGR.end(), VK_FORMAT_R8G8B8A8_UNORM) != formatsBGR.end()); + + // ppm binary pixel data + for (int32_t y = 0; y < height; y++) { + unsigned int* row = (unsigned int*)imageData; + for (int32_t x = 0; x < width; x++) { + if (colorSwizzle) { + file.write((char*)row + 2, 1); + file.write((char*)row + 1, 1); + file.write((char*)row, 1); + } + else { + file.write((char*)row, 3); + } + row++; + } + imageData += subResourceLayout.rowPitch; + } + file.close(); + + std::cout << "Framebuffer image saved to " << filePath << fileName << std::endl; + + // Clean up resources + vkUnmapMemory(device, dstImageMemory); + vkFreeMemory(device, dstImageMemory, nullptr); + vkDestroyImage(device, dstImage, nullptr); + + } + + void PlumageRender::outputImageSequence(VkImage image, std::string filePath) + { + for (uint32_t i = 0; i < settings.outputFrameCount; i++) + { + std::string fileName = std::to_string(i) + "result.pmm"; + std::string filePath = std::to_string(selectedPhysicalDeviceIndex); + } + } + + uint32_t PlumageRender::getMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) + { + VkPhysicalDeviceMemoryProperties deviceMemoryProperties; + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties); + for (uint32_t i = 0; i < deviceMemoryProperties.memoryTypeCount; i++) + { + if ((typeBits & 1) == 1) + { + if ((deviceMemoryProperties.memoryTypes[i].propertyFlags & properties) == properties) + { + return i; + } + } + typeBits >>= 1; + } + return 0; + } + void PlumageRender::render() { if (!prepared) { @@ -1579,7 +1726,9 @@ PlumageRender::PlumageRender() } updateUIOverlay(); - + //加入写到文件的函数 + //swapChainImage = swapChain.images[frameIndex]; + //outputImageSequeue(swapChainImage,filePath.imageSequenceFilePath); VK_CHECK_RESULT(vkWaitForFences(device, 1, &waitFences[frameIndex], VK_TRUE, UINT64_MAX)); VK_CHECK_RESULT(vkResetFences(device, 1, &waitFences[frameIndex])); @@ -1589,6 +1738,8 @@ PlumageRender::PlumageRender() } else { VK_CHECK_RESULT(acquire); + + } // Update UBOs @@ -1610,6 +1761,7 @@ PlumageRender::PlumageRender() submitInfo.commandBufferCount = 1; VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, waitFences[frameIndex])); + //显示队列 VkResult present = swapChain.queuePresent(queue, currentBuffer, renderCompleteSemaphores[frameIndex]); if (!((present == VK_SUCCESS) || (present == VK_SUBOPTIMAL_KHR))) { if (present == VK_ERROR_OUT_OF_DATE_KHR) { @@ -1625,7 +1777,7 @@ PlumageRender::PlumageRender() frameIndex %= renderAhead; if (!paused) { - if (rotateModel) { + if (settings.rotateModel) { modelrot.y += frameTimer * 35.0f; if (modelrot.y > 360.0f) { modelrot.y -= 360.0f; @@ -1639,13 +1791,15 @@ PlumageRender::PlumageRender() models.scene.updateAnimation(animationIndex, animationTimer); } updateShaderData(); - if (rotateModel) { + if (settings.rotateModel) { updateUniformBuffers(); } } if (camera.updated) { updateUniformBuffers(); } + + } diff --git a/src/render/render.h b/src/render/render.h index 269536d..eff1be4 100644 --- a/src/render/render.h +++ b/src/render/render.h @@ -159,11 +159,18 @@ public: //ttf file path std::string ttfFilePath = getAssetPath() + "/data/Roboto-Medium.ttf"; + // output file path + // imageSequence + std::string imageSequenceFilePath = getAssetPath() + "/output/imageSequence"; + // screen shot image + std::string screenShotFilePath = getAssetPath() + "/output/screenShot"; + } filePath; - bool rotateModel = false; + glm::vec3 modelrot = glm::vec3(0.0f); glm::vec3 modelPos = glm::vec3(0.0f); + enum PBRWorkflows { PBR_WORKFLOW_METALLIC_ROUGHNESS = 0, PBR_WORKFLOW_SPECULAR_GLOSINESS = 1 }; @@ -214,6 +221,11 @@ public: bool fullscreen = false; bool vsync = false; bool multiSampling = true; + bool rotateModel = true; + bool enableSaveToImageSequeue = false; + uint32_t outputFrameCount = 50; + bool takeScreenShot = false; + VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_4_BIT; } settings; @@ -235,6 +247,8 @@ public: const uint32_t renderAhead = 2; uint32_t frameIndex = 0; + //VkImage swapChainImage; + int32_t animationIndex = 0; float animationTimer = 0.0f; bool animate = true; @@ -331,6 +345,11 @@ public: void updateShaderData(); void windowResized(); void prepare(); + void submitWork(VkCommandBuffer cmdBuffer, VkQueue queue); + void writeImageToFile(std::string filePath, std::string fileName); + void outputImageSequence(VkImage image,std::string filePath); + void outputScreenShot(VkImage image, std::string filePath); + uint32_t getMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties); virtual void render(); virtual void updateUIOverlay(); virtual void fileDropped(std::string filename);