diff --git a/visigen/CMakeLists.txt b/visigen/CMakeLists.txt index 46660564b..8d3a1e53d 100644 --- a/visigen/CMakeLists.txt +++ b/visigen/CMakeLists.txt @@ -28,22 +28,31 @@ else() endif() if(APPLE) - target_sources(visigen PRIVATE MainMac.mm VISIRendererMetal.mm VISIRendererMetal.hh) - set_source_files_properties(MainMac.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) - set_source_files_properties(VISIRendererMetal.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) + target_sources(visigen PRIVATE main.cpp metal/VISIRendererMetal.mm metal/VISIRendererMetal.hh) + set_source_files_properties(metal/VISIRendererMetal.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) find_library(METAL_LIBRARY Metal REQUIRED) target_link_libraries(visigen PRIVATE ${METAL_LIBRARY}) elseif(WIN32) target_sources(visigen PRIVATE MainWin.cpp - VISIRendererOpenGL.cpp - VISIRendererOpenGL.hpp) -else() + opengl/VISIRendererOpenGL.cpp + opengl/VISIRendererOpenGL.hpp + vulkan/VISIRendererVulkan.cpp + vulkan/VISIRendererVulkan.hpp + vulkan/utils.cpp) +else () + bintoc(vk_fs.cpp vulkan/shader.frag.spv VK_FRAGMENT_SPV) + bintoc(vk_vs.cpp vulkan/shader.vert.spv VK_VERTEX_SPV) target_sources(visigen PRIVATE - MainXlib.cpp - VISIRendererOpenGL.cpp - VISIRendererOpenGL.hpp) -endif() + main.cpp + opengl/VISIRendererOpenGL.cpp + opengl/VISIRendererOpenGL.hpp + vulkan/VISIRendererVulkan.cpp + vulkan/VISIRendererVulkan.hpp + vulkan/utils.cpp + vk_fs.cpp + vk_vs.cpp) +endif () target_link_libraries(visigen PRIVATE athena-core diff --git a/visigen/MainWin.cpp b/visigen/MainWin.cpp index e170c94f6..e34897db7 100644 --- a/visigen/MainWin.cpp +++ b/visigen/MainWin.cpp @@ -1,4 +1,4 @@ -#include "VISIRendererOpenGL.hpp" +#include "opengl/VISIRendererOpenGL.hpp" #include #include #include diff --git a/visigen/MainXlib.cpp b/visigen/MainXlib.cpp index 30147325f..6293b0f0a 100644 --- a/visigen/MainXlib.cpp +++ b/visigen/MainXlib.cpp @@ -1,4 +1,4 @@ -#include "VISIRendererOpenGL.hpp" +#include "opengl/VISIRendererOpenGL.hpp" #include #include #include diff --git a/visigen/Shader.metal b/visigen/Shader.metal deleted file mode 100644 index 4759f48b5..000000000 --- a/visigen/Shader.metal +++ /dev/null @@ -1,68 +0,0 @@ -#include -#include - -#include "ShaderTypes.h" - -using namespace metal; - -/*static const matrix_float4x4 LookMATs[] = { - {// Forward - {1.f, 0.f, 0.f, 0.f}, - {0.f, 0.f, 1.f, 0.f}, - {0.f, -1.f, 0.f, 0.f}, - {0.f, 0.f, 0.f, 1.f}}, - {// Backward - {-1.f, 0.f, 0.f, 0.f}, - {0.f, 0.f, 1.f, 0.f}, - {0.f, 1.f, 0.f, 0.f}, - {0.f, 0.f, 0.f, 1.f}}, - {// Up - {1.f, 0.f, 0.f, 0.f}, - {0.f, -1.f, 0.f, 0.f}, - {0.f, 0.f, -1.f, 0.f}, - {0.f, 0.f, 0.f, 1.f}}, - {// Down - {1.f, 0.f, 0.f, 0.f}, - {0.f, 1.f, 0.f, 0.f}, - {0.f, 0.f, 1.f, 0.f}, - {0.f, 0.f, 0.f, 1.f}}, - {// Left - {0.f, 1.f, 0.f, 0.f}, - {0.f, 0.f, 1.f, 0.f}, - {1.f, 0.f, 0.f, 0.f}, - {0.f, 0.f, 0.f, 1.f}}, - {// Right - {0.f, -1.f, 0.f, 0.f}, - {0.f, 0.f, 1.f, 0.f}, - {-1.f, 0.f, 0.f, 0.f}, - {0.f, 0.f, 0.f, 1.f}}, -};*/ - -typedef struct -{ - float4 position [[position]]; - float4 color; -} ColorInOut; - -typedef struct -{ - float3 position [[attribute(VertexAttributePosition)]]; - float4 color [[attribute(VertexAttributeColor)]]; -} Vertex; - -vertex ColorInOut vertexShader(Vertex in [[stage_in]], constant Uniforms& uniforms [[buffer(BufferIndexUniforms)]]) -{ - ColorInOut out; - - float4 position = float4(in.position, 1.0); - position.y *= -1.f; - out.position = uniforms.projectionMatrix * uniforms.modelViewMatrix * position; - out.color = in.color; - - return out; -} - -fragment float4 fragmentShader(ColorInOut in [[stage_in]]) -{ - return in.color; -} \ No newline at end of file diff --git a/visigen/VISIBuilder.cpp b/visigen/VISIBuilder.cpp index 20eb96155..cd61a3def 100644 --- a/visigen/VISIBuilder.cpp +++ b/visigen/VISIBuilder.cpp @@ -31,15 +31,15 @@ const VISIBuilder::Leaf& VISIBuilder::PVSRenderCache::GetLeaf(const zeus::CVecto bool needsTransparent = false; m_renderer.RenderPVSOpaque(RGBABuf.get(), needsTransparent); -// size_t outsize; -// auto* buf = VISIRenderer::makePNGBuffer(reinterpret_cast(RGBABuf.get()), 768, 512, &outsize); -// auto filename = fmt::format(FMT_STRING("outx{}.png"), m_frame++); -// std::cout << "Rendering " << filename << std::endl; -// std::ofstream fout; -// fout.open(filename, std::ios::binary | std::ios::out); -// fout.write(static_cast(buf), outsize); -// fout.close(); -// free(buf); + size_t outsize; + auto* buf = VISIRenderer::makePNGBuffer(reinterpret_cast(RGBABuf.get()), 768, 512, &outsize); + auto filename = fmt::format(FMT_STRING("/tmp/visigen/outx{}.png"), m_frame++); + std::cout << "Rendering " << filename << std::endl; + std::ofstream fout; + fout.open(filename, std::ios::binary | std::ios::out); + fout.write(static_cast(buf), outsize); + fout.close(); + free(buf); std::unique_ptr leafOut = std::make_unique(); for (unsigned i = 0; i < 768 * 512; ++i) { diff --git a/visigen/VISIRenderer.hpp b/visigen/VISIRenderer.hpp index 948eb3266..7db6b3af5 100644 --- a/visigen/VISIRenderer.hpp +++ b/visigen/VISIRenderer.hpp @@ -70,7 +70,7 @@ public: }; VISIRenderer(int argc, const hecl::SystemChar** argv) : m_argc(argc), m_argv(argv) {} - void Run(FPercent updatePercent); + virtual void Run(FPercent updatePercent); void Terminate(); virtual void RenderPVSOpaque(RGBA8* bufOut, bool& needTransparent) = 0; virtual void RenderPVSTransparent(const std::function& passFunc) = 0; diff --git a/visigen/MainMac.mm b/visigen/main.cpp similarity index 77% rename from visigen/MainMac.mm rename to visigen/main.cpp index 003dde4ff..e89e75f7e 100644 --- a/visigen/MainMac.mm +++ b/visigen/main.cpp @@ -1,13 +1,10 @@ #include "../version.h" -#include "VISIRendererMetal.hh" #include "athena/Global.hpp" #include "logvisor/logvisor.hpp" -#include -#include -#include - -#if !__has_feature(objc_arc) -#error ARC Required +#ifdef __APPLE__ +#include "metal/VISIRendererMetal.hh" +#else +#include "vulkan/VISIRendererVulkan.hpp" #endif static logvisor::Module AthenaLog("Athena"); @@ -25,9 +22,11 @@ int main(int argc, const char **argv) { logvisor::RegisterStandardExceptions(); logvisor::RegisterConsoleLogger(); atSetExceptionHandler(AthenaExc); +#ifdef __APPLE__ VISIRendererMetal renderer(argc, argv); - @autoreleasepool { - renderer.Run(nullptr); - } +#else + VISIRendererVulkan renderer(argc, argv); +#endif + renderer.Run(nullptr); return renderer.ReturnVal(); } diff --git a/visigen/metal/Shader.metal b/visigen/metal/Shader.metal new file mode 100644 index 000000000..e00fa9641 --- /dev/null +++ b/visigen/metal/Shader.metal @@ -0,0 +1,34 @@ +#include +#include + +#include "ShaderTypes.h" + +using namespace metal; + +typedef struct +{ + float4 position [[position]]; + float4 color; +} ColorInOut; + +typedef struct +{ + float3 position [[attribute(VertexAttributePosition)]]; + float4 color [[attribute(VertexAttributeColor)]]; +} Vertex; + +vertex ColorInOut vertexShader(Vertex in [[stage_in]], constant Uniforms& uniforms [[buffer(BufferIndexUniforms)]]) +{ + ColorInOut out; + + float4 position = float4(in.position, 1.0); + out.position = uniforms.projectionMatrix * uniforms.modelViewMatrix * position; + out.color = in.color; + + return out; +} + +fragment float4 fragmentShader(ColorInOut in [[stage_in]]) +{ + return in.color; +} diff --git a/visigen/ShaderTypes.h b/visigen/metal/ShaderTypes.h similarity index 100% rename from visigen/ShaderTypes.h rename to visigen/metal/ShaderTypes.h diff --git a/visigen/VISIRendererMetal.hh b/visigen/metal/VISIRendererMetal.hh similarity index 84% rename from visigen/VISIRendererMetal.hh rename to visigen/metal/VISIRendererMetal.hh index bbdeb2b94..d074d19e9 100644 --- a/visigen/VISIRendererMetal.hh +++ b/visigen/metal/VISIRendererMetal.hh @@ -1,9 +1,11 @@ #pragma once -#include "VISIRenderer.hpp" -#import +#include "../VISIRenderer.hpp" #import -#import + +#if !__has_feature(objc_arc) +#error ARC Required +#endif @interface MetalRenderer : NSObject @end @@ -19,8 +21,9 @@ public: VISIRendererMetal(int argc, const hecl::SystemChar** argv) : VISIRenderer(argc, argv) { view = [[MetalRenderer alloc] init]; } + void Run(FPercent updatePercent) override; void RenderPVSOpaque(RGBA8* out, bool& needTransparent) override; void RenderPVSTransparent(const std::function& passFunc) override; void RenderPVSEntitiesAndLights(const std::function& passFunc, const std::function& lightPassFunc) override; -}; \ No newline at end of file +}; diff --git a/visigen/VISIRendererMetal.mm b/visigen/metal/VISIRendererMetal.mm similarity index 97% rename from visigen/VISIRendererMetal.mm rename to visigen/metal/VISIRendererMetal.mm index 66927c12e..607c2ba01 100644 --- a/visigen/VISIRendererMetal.mm +++ b/visigen/metal/VISIRendererMetal.mm @@ -5,8 +5,11 @@ static zeus::CMatrix4f g_Proj; -constexpr zeus::CMatrix4f VulkanCorrect(1.f, 0.f, 0.f, 0.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 0.5f, 0.5f + FLT_EPSILON, - 0.f, 0.f, 0.f, 1.f); +constexpr zeus::CMatrix4f DepthCorrect( + 1.f, 0.f, 0.f, 0.f, + 0.f, 1.f, 0.f, 0.f, + 0.f, 0.f, 0.5f, 0.5f, + 0.f, 0.f, 0.f, 1.f); static void CalculateProjMatrix() { float znear = 0.2f; @@ -25,9 +28,11 @@ static void CalculateProjMatrix() { float fmn = zfar - znear; zeus::CMatrix4f mat2{ - 2.f * znear / rml, 0.f, rpl / rml, 0.f, 0.f, 2.f * znear / tmb, tpb / tmb, 0.f, 0.f, 0.f, -fpn / fmn, - -2.f * zfar * znear / fmn, 0.f, 0.f, -1.f, 0.f}; - g_Proj = VulkanCorrect * mat2; + 2.f * znear / rml, 0.f, rpl / rml, 0.f, + 0.f, 2.f * znear / tmb, tpb / tmb, 0.f, + 0.f, 0.f, -fpn / fmn, -2.f * zfar * znear / fmn, + 0.f, 0.f, -1.f, 0.f}; + g_Proj = DepthCorrect * mat2; } static constexpr std::array AABBIdxs{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 1, 7, 3, 5, 5, 0, 0, 2, 6, 4}; @@ -238,8 +243,8 @@ using Vertex = VISIRenderer::Model::Vert; [encoder setVertexBuffer:_uniformBuffer offset:0 atIndex:BufferIndexUniforms]; for (int j = 0; j < 6; ++j) { - GLint x = (j % 3) * 256; - GLint y = (j / 3) * 256; + NSUInteger x = (j % 3) * 256; + NSUInteger y = (j / 3) * 256; [encoder setViewport:{x, y, 256, 256, 0, 1}]; if (j > 0) { [encoder setVertexBufferOffset:j * sizeof(Uniforms) atIndex:BufferIndexUniforms]; @@ -404,7 +409,7 @@ using Vertex = VISIRenderer::Model::Vert; [encoder setVisibilityResultMode:MTLVisibilityResultModeBoolean offset:queryCount * sizeof(uint64_t)]; [encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangleStrip indexCount:20 - indexType:MTLIndexTypeUInt32 + indexType:MTLIndexTypeUInt16 indexBuffer:_aabbIndexBuffer indexBufferOffset:0]; } @@ -454,6 +459,12 @@ using Vertex = VISIRenderer::Model::Vert; } @end +void VISIRendererMetal::Run(FPercent updatePercent) { + @autoreleasepool { + VISIRenderer::Run(updatePercent); + } +} + bool VISIRendererMetal::SetupShaders() { return [view setup]; } bool VISIRendererMetal::SetupVertexBuffersAndFormats() { @@ -477,4 +488,4 @@ void VISIRendererMetal::RenderPVSEntitiesAndLights(const std::function @@ -39,4 +39,4 @@ public: void RenderPVSTransparent(const std::function& passFunc) override; void RenderPVSEntitiesAndLights(const std::function& passFunc, const std::function& lightPassFunc) override; -}; \ No newline at end of file +}; diff --git a/visigen/vulkan/VISIRendererVulkan.cpp b/visigen/vulkan/VISIRendererVulkan.cpp new file mode 100644 index 000000000..28aa8296b --- /dev/null +++ b/visigen/vulkan/VISIRendererVulkan.cpp @@ -0,0 +1,342 @@ +#include "VISIRendererVulkan.hpp" +#include "utils.hpp" + +extern "C" { +extern const uint8_t VK_FRAGMENT_SPV[]; +extern const size_t VK_FRAGMENT_SPV_SZ; +extern const uint8_t VK_VERTEX_SPV[]; +extern const size_t VK_VERTEX_SPV_SZ; +} + +static char const* AppName = "VISIGen"; + +static zeus::CMatrix4f g_Proj; + +constexpr zeus::CMatrix4f DepthCorrect(1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.5f, 0.5f, 0.f, 0.f, 0.f, + 1.f); + +static void CalculateProjMatrix() { + float znear = 0.2f; + float zfar = 1000.f; + float tfov = std::tan(zeus::degToRad(90.f * 0.5f)); + float top = znear * tfov; + float bottom = -top; + float right = znear * tfov; + float left = -right; + + float rml = right - left; + float rpl = right + left; + float tmb = top - bottom; + float tpb = top + bottom; + float fpn = zfar + znear; + float fmn = zfar - znear; + + zeus::CMatrix4f mat2{ + 2.f * znear / rml, 0.f, rpl / rml, 0.f, 0.f, 2.f * znear / tmb, tpb / tmb, 0.f, 0.f, 0.f, -fpn / fmn, + -2.f * zfar * znear / fmn, 0.f, 0.f, -1.f, 0.f}; + g_Proj = DepthCorrect * mat2; +} + +static constexpr std::array AABBIdxs{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 1, 7, 3, 5, 5, 0, 0, 2, 6, 4}; + +static const zeus::CMatrix4f LookMATs[] = { + {// Forward + 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f}, + {// Backward + -1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f}, + {// Up + 1.f, 0.f, 0.f, 0.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 1.f}, + {// Down + 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f}, + {// Left + 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f}, + {// Right + 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f}, +}; + +template +static vk::UniqueShaderModule createShaderModule(const vk::UniqueDevice& device, const T* code, size_t size) { + return vk::su::assertSuccess(device->createShaderModuleUnique( + vk::ShaderModuleCreateInfo().setCodeSize(size).setPCode(reinterpret_cast(code)))); +} + +static int rateDeviceSuitability(const vk::PhysicalDevice& device) { + int score = 0; + const auto deviceProperties = device.getProperties(); + if (deviceProperties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) { + score += 1000; + } else if (deviceProperties.deviceType == vk::PhysicalDeviceType::eIntegratedGpu) { + score += 100; + } + // const auto deviceFeatures = device.getFeatures(); + // if (!deviceFeatures.geometryShader) { + // return 0; + // } + return score; +} + +static const vk::PhysicalDevice& pickPhysicalDevice(const std::vector& devices) { + std::multimap candidates; + for (const auto& device : devices) { + int score = rateDeviceSuitability(device); + candidates.insert(std::make_pair(score, device)); + } + if (candidates.rbegin()->first > 0) { + return candidates.rbegin()->second; + } + assert(false && "failed to find a suitable GPU!"); + return candidates.begin()->second; +} + +bool VISIRendererVulkan::SetupShaders() { + instance = vk::su::createInstance(AppName, AppName, {}, vk::su::getInstanceExtensions()); + physicalDevice = pickPhysicalDevice(vk::su::assertSuccess(instance->enumeratePhysicalDevices())); + auto queueFamilyIndex = vk::su::findGraphicsQueueFamilyIndex(physicalDevice.getQueueFamilyProperties()); + device = vk::su::createDevice(physicalDevice, queueFamilyIndex, {}); + commandPool = vk::su::createCommandPool(device.get(), queueFamilyIndex); + commandBuffer = vk::su::createCommandBuffer(device.get(), commandPool.get()); + graphicsQueue = device->getQueue(queueFamilyIndex, 0); + vertexShader = createShaderModule(device, static_cast(VK_VERTEX_SPV), VK_VERTEX_SPV_SZ); + fragmentShader = createShaderModule(device, static_cast(VK_FRAGMENT_SPV), VK_FRAGMENT_SPV_SZ); + vk::Format colorFormat = vk::Format::eR8G8B8A8Unorm; + vk::Format depthFormat = vk::Format::eD16Unorm; + colorRenderPass = vk::su::createRenderPass(device.get(), colorFormat, depthFormat, vk::AttachmentLoadOp::eClear, + vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eClear, + vk::AttachmentStoreOp::eStore, vk::ImageLayout::eTransferSrcOptimal); + depthRenderPass = vk::su::createRenderPass(device.get(), colorFormat, depthFormat, vk::AttachmentLoadOp::eDontCare, + vk::AttachmentStoreOp::eDontCare, vk::AttachmentLoadOp::eLoad, + vk::AttachmentStoreOp::eStore, vk::ImageLayout::eGeneral); + pipelineCache = vk::su::assertSuccess(device->createPipelineCacheUnique(vk::PipelineCacheCreateInfo())); + + descriptorSetLayout = vk::su::createDescriptorSetLayout( + device.get(), {{vk::DescriptorType::eUniformBufferDynamic, 1, vk::ShaderStageFlagBits::eVertex}}); + descriptorPool = vk::su::createDescriptorPool(device.get(), + {vk::DescriptorPoolSize(vk::DescriptorType::eUniformBufferDynamic, 1)}); + pipelineLayout = vk::su::assertSuccess( + device->createPipelineLayoutUnique(vk::PipelineLayoutCreateInfo({}, descriptorSetLayout.get()))); + std::pair vertexShaderData{vertexShader.get(), nullptr}; + std::pair fragmentShaderData{fragmentShader.get(), nullptr}; + std::vector> vertexInputAttributeFormats{ + {vk::Format::eR32G32B32Sfloat, offsetof(Vertex, pos)}, + {vk::Format::eR32G32B32A32Sfloat, offsetof(Vertex, color)}, + }; + colorPipeline = vk::su::createGraphicsPipeline( + device.get(), pipelineCache.get(), vertexShaderData, fragmentShaderData, sizeof(Vertex), + vertexInputAttributeFormats, vk::FrontFace::eClockwise, true, pipelineLayout.get(), colorRenderPass.get()); + depthPipeline = vk::su::createGraphicsPipeline( + device.get(), pipelineCache.get(), vertexShaderData, fragmentShaderData, sizeof(Vertex), + vertexInputAttributeFormats, vk::FrontFace::eClockwise, true, pipelineLayout.get(), depthRenderPass.get()); + m_extent = vk::Extent2D(768, 512); + colorAttachment = std::make_unique( + physicalDevice, device.get(), colorFormat, m_extent, vk::ImageTiling::eOptimal, + vk::ImageUsageFlags{vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc}, + vk::ImageLayout::eUndefined, vk::MemoryPropertyFlags{vk::MemoryPropertyFlagBits::eDeviceLocal}, + vk::ImageAspectFlags{vk::ImageAspectFlagBits::eColor}); + colorAttachmentRead = std::make_unique( + physicalDevice, device.get(), colorFormat, m_extent, vk::ImageTiling::eLinear, + vk::ImageUsageFlags{vk::ImageUsageFlagBits::eTransferDst}, vk::ImageLayout::eUndefined, + vk::MemoryPropertyFlags{vk::MemoryPropertyFlagBits::eHostVisible}, + vk::ImageAspectFlags{vk::ImageAspectFlagBits::eColor}); + // depthAttachment = std::make_unique( + // physicalDevice, device.get(), vk::Format::eD16Unorm, m_extent, vk::ImageTiling::eOptimal, + // vk::ImageUsageFlags{vk::ImageUsageFlagBits::eDepthStencilAttachment}, vk::ImageLayout::eUndefined, + // vk::MemoryPropertyFlags{vk::MemoryPropertyFlagBits::eDeviceLocal}, + // vk::ImageAspectFlags{vk::ImageAspectFlagBits::eDepth}); + depthAttachment = std::make_unique(physicalDevice, device.get(), depthFormat, m_extent); + colorFramebuffer = vk::su::createFramebuffer(device.get(), colorRenderPass.get(), colorAttachment->imageView.get(), + depthAttachment->imageView.get(), m_extent); + depthFramebuffer = vk::su::createFramebuffer(device.get(), depthRenderPass.get(), colorAttachment->imageView.get(), + depthAttachment->imageView.get(), m_extent); + return true; +} + +bool VISIRendererVulkan::SetupVertexBuffersAndFormats() { + size_t vertCount = 0; + size_t indexCount = 0; + for (const auto& model : m_models) { + vertCount += model.verts.size(); + indexCount += model.idxs.size(); + } + m_entityVertStart = vertCount; + vertCount += 8 * m_entities.size(); + vertCount += m_lights.size(); + vertexBuffer = createBuffer(vertCount * sizeof(Vertex), {vk::BufferUsageFlagBits::eVertexBuffer}); + indexBuffer = createBuffer(indexCount * sizeof(uint32_t), {vk::BufferUsageFlagBits::eIndexBuffer}); + + auto* vertMap = static_cast(vertexBuffer->map(device.get())); + auto* indexMap = static_cast(indexBuffer->map(device.get())); + for (const auto& model : m_models) { + memcpy(vertMap, model.verts.data(), model.verts.size() * sizeof(Vertex)); + memcpy(indexMap, model.idxs.data(), model.idxs.size() * sizeof(uint32_t)); + vertMap += model.verts.size(); + indexMap += model.idxs.size(); + } + auto idx = static_cast(m_models.size()); + for (const auto& ent : m_entities) { + auto verts = VISIRenderer::AABBToVerts(ent.aabb, VISIRenderer::ColorForIndex(idx++)); + memcpy(vertMap, verts.data(), verts.size() * sizeof(Vertex)); + vertMap += verts.size(); + } + for (const auto& light : m_lights) { + auto* vert = vertMap++; + vert->pos = light.point; + vert->color = VISIRenderer::ColorForIndex(idx++); + } + vertexBuffer->unmap(device.get()); + indexBuffer->unmap(device.get()); + + uniformBuffer = createBuffer(sizeof(Uniforms) * 6, {vk::BufferUsageFlagBits::eUniformBuffer}); + aabbIndexBuffer = createBuffer(AABBIdxs.size() * sizeof(uint16_t), {vk::BufferUsageFlagBits::eIndexBuffer}); + aabbIndexBuffer->upload(device.get(), AABBIdxs); + + { + const std::array layouts{descriptorSetLayout.get()}; + uniformBufferDescriptorSet = + std::move(vk::su::assertSuccess(device->allocateDescriptorSetsUnique( + vk::DescriptorSetAllocateInfo{descriptorPool.get(), layouts})) + .front()); + } + vk::su::updateDescriptorSets( + device.get(), uniformBufferDescriptorSet.get(), + {{vk::DescriptorType::eUniformBufferDynamic, uniformBuffer->buffer.get(), sizeof(Uniforms)}}, {}); + return true; +} + +void VISIRendererVulkan::SetupRenderPass(const zeus::CVector3f& pos) { + auto posMat = zeus::CTransform::Translate(-pos).toMatrix4f(); + auto* buffer = static_cast(uniformBuffer->map(device.get())); + for (uint16_t j = 0; j < 6; ++j) { + zeus::CMatrix4f modelView = LookMATs[j] * posMat; + m_frustums[j].updatePlanes(modelView, g_Proj); + buffer->projectionMatrix = g_Proj; + buffer->modelViewMatrix = modelView; + buffer++; + } + uniformBuffer->unmap(device.get()); +} + +void VISIRendererVulkan::RenderPVSOpaque(VISIRenderer::RGBA8* out, bool& needTransparent) { + commandBuffer->begin(vk::CommandBufferBeginInfo{}); + + { + std::array clearValues{ + vk::ClearColorValue(std::array{0.f, 0.f, 0.f, 1.f}), + vk::ClearDepthStencilValue(1.f, 0), + }; + vk::RenderPassBeginInfo renderPassBeginInfo(colorRenderPass.get(), colorFramebuffer.get(), + vk::Rect2D{{0, 0}, m_extent}, clearValues.size(), clearValues.data()); + commandBuffer->beginRenderPass(renderPassBeginInfo, vk::SubpassContents::eInline); + } + commandBuffer->bindPipeline(vk::PipelineBindPoint::eGraphics, colorPipeline.get()); + commandBuffer->setScissor(0, std::array{vk::Rect2D{{}, m_extent}}); + // commandBuffer->bindVertexBuffers(0, std::array{vertexBuffer->buffer.get()}, std::array{0}); + commandBuffer->bindIndexBuffer(indexBuffer->buffer.get(), 0, vk::IndexType::eUint32); + + for (uint32_t j = 0; j < 6; ++j) { + auto x = static_cast((j % 3) * 256); + auto y = static_cast((j / 3) * 256); // NOLINT(bugprone-integer-division) + commandBuffer->setViewport(0, std::array{vk::Viewport{x, y, 256.f, 256.f, 0.f, 1.f}}); + commandBuffer->bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout.get(), 0, + std::array{uniformBufferDescriptorSet.get()}, + std::array{j * sizeof(Uniforms)}); + size_t vertexBufferOffset = 0; + size_t indexBufferOffset = 0; + for (const auto& model : m_models) { + if (m_frustums[j].aabbFrustumTest(model.aabb)) { + commandBuffer->bindVertexBuffers(0, std::array{vertexBuffer->buffer.get()}, + std::array{vertexBufferOffset}); + for (const auto& surf : model.surfaces) { + // Non-transparents first + if (surf.transparent) { + needTransparent = true; + } else { + assert(model.topology == hecl::HMDLTopology::TriStrips); + commandBuffer->drawIndexed(surf.count, 1, indexBufferOffset + surf.first, 0, 0); + } + } + } + vertexBufferOffset += model.verts.size() * sizeof(Vertex); + indexBufferOffset += model.idxs.size(); + } + } + + commandBuffer->endRenderPass(); + + { +// const vk::ImageMemoryBarrier transferSrcBarrier{ +// vk::AccessFlagBits::eMemoryRead, +// vk::AccessFlagBits::eTransferRead, +// vk::ImageLayout::eGeneral, +// vk::ImageLayout::eTransferSrcOptimal, +// 0, +// 0, +// colorAttachment->image.get(), +// vk::ImageSubresourceRange{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}, +// }; + const vk::ImageMemoryBarrier transferDstBarrier{ + vk::AccessFlags{}, + vk::AccessFlagBits::eTransferWrite, + vk::ImageLayout::eUndefined, + vk::ImageLayout::eTransferDstOptimal, + 0, + 0, + colorAttachmentRead->image.get(), + vk::ImageSubresourceRange{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}, + }; + commandBuffer->pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eTransfer, + vk::DependencyFlags{}, std::array{}, + std::array{}, +// std::array{transferSrcBarrier, transferDstBarrier} + std::array{transferDstBarrier} + ); + } + { + const vk::ImageCopy imageCopy{vk::ImageSubresourceLayers{vk::ImageAspectFlagBits::eColor, 0, 0, 1}, vk::Offset3D{}, + vk::ImageSubresourceLayers{vk::ImageAspectFlagBits::eColor, 0, 0, 1}, vk::Offset3D{}, + vk::Extent3D{m_extent, 1}}; + commandBuffer->copyImage(colorAttachment->image.get(), vk::ImageLayout::eTransferSrcOptimal, + colorAttachmentRead->image.get(), vk::ImageLayout::eTransferDstOptimal, + std::array{imageCopy}); + } +// { +//// const vk::ImageMemoryBarrier transferSrcBarrier{ +//// vk::AccessFlagBits::eTransferRead, +//// vk::AccessFlags{}, +//// vk::ImageLayout::eTransferSrcOptimal, +//// vk::ImageLayout::eColorAttachmentOptimal, +//// 0, +//// 0, +//// colorAttachment->image.get(), +//// vk::ImageSubresourceRange{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}, +//// }; +// const vk::ImageMemoryBarrier transferDstBarrier{ +// vk::AccessFlagBits::eTransferWrite, +// vk::AccessFlagBits::eHostRead, +// vk::ImageLayout::eTransferDstOptimal, +// vk::ImageLayout::eGeneral, +// 0, +// 0, +// colorAttachmentRead->image.get(), +// vk::ImageSubresourceRange{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}, +// }; +// commandBuffer->pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, +// vk::PipelineStageFlagBits::eHost, vk::DependencyFlags{}, +// std::array{}, std::array{}, +//// std::array{transferSrcBarrier, transferDstBarrier} +// std::array{transferDstBarrier} +// ); +// } + commandBuffer->end(); + vk::su::submitAndWait(device.get(), graphicsQueue, commandBuffer.get()); + + size_t size = sizeof(VISIRenderer::RGBA8) * m_extent.height * m_extent.width; + assert(size == colorAttachmentRead->deviceSize); + void* imageMemory = vk::su::assertSuccess(device->mapMemory(colorAttachmentRead->deviceMemory.get(), 0, size)); + memcpy(out, imageMemory, size); + device->unmapMemory(colorAttachmentRead->deviceMemory.get()); +} + +void VISIRendererVulkan::RenderPVSTransparent(const std::function& passFunc) {} + +void VISIRendererVulkan::RenderPVSEntitiesAndLights(const std::function& passFunc, + const std::function& lightPassFunc) {} diff --git a/visigen/vulkan/VISIRendererVulkan.hpp b/visigen/vulkan/VISIRendererVulkan.hpp new file mode 100644 index 000000000..0694a30e6 --- /dev/null +++ b/visigen/vulkan/VISIRendererVulkan.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include "../VISIRenderer.hpp" + +#include "utils.hpp" + +#include + +class VISIRendererVulkan : public VISIRenderer { + vk::PhysicalDevice physicalDevice; + vk::UniqueInstance instance; + vk::UniqueDevice device; + vk::UniqueCommandPool commandPool; + vk::UniqueCommandBuffer commandBuffer; + vk::Queue graphicsQueue; + vk::UniqueShaderModule vertexShader; + vk::UniqueShaderModule fragmentShader; + vk::UniqueRenderPass colorRenderPass; + vk::UniqueRenderPass depthRenderPass; + vk::UniquePipelineCache pipelineCache; + vk::UniquePipeline colorPipeline; + vk::UniquePipeline depthPipeline; + vk::UniqueDescriptorSetLayout descriptorSetLayout; + vk::UniqueDescriptorPool descriptorPool; + vk::UniqueDescriptorSet uniformBufferDescriptorSet; + vk::UniquePipelineLayout pipelineLayout; + std::unique_ptr colorAttachment; + std::unique_ptr colorAttachmentRead; + std::unique_ptr depthAttachment; + std::unique_ptr uniformBuffer; + std::unique_ptr vertexBuffer; + std::unique_ptr indexBuffer; + std::unique_ptr aabbIndexBuffer; + vk::UniqueFramebuffer colorFramebuffer; + vk::UniqueFramebuffer depthFramebuffer; + + bool SetupShaders() override; + bool SetupVertexBuffersAndFormats() override; + void SetupRenderPass(const zeus::CVector3f& pos) override; + +private: + using Vertex = VISIRenderer::Model::Vert; + using Uniforms = struct { + zeus::CMatrix4f projectionMatrix; + zeus::CMatrix4f modelViewMatrix; + }; + size_t m_entityVertStart; + std::array m_frustums; + vk::Extent2D m_extent; + + inline std::unique_ptr createBuffer(size_t size, vk::BufferUsageFlags usageFlags) { + return std::make_unique(physicalDevice, device.get(), size, usageFlags); + } + +public: + VISIRendererVulkan(int argc, const hecl::SystemChar** argv) : VISIRenderer(argc, argv) {} + void RenderPVSOpaque(RGBA8* out, bool& needTransparent) override; + void RenderPVSTransparent(const std::function& passFunc) override; + void RenderPVSEntitiesAndLights(const std::function& passFunc, + const std::function& lightPassFunc) override; +}; diff --git a/visigen/vulkan/shader.frag b/visigen/vulkan/shader.frag new file mode 100644 index 000000000..3beed0c39 --- /dev/null +++ b/visigen/vulkan/shader.frag @@ -0,0 +1,14 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +struct VertToFrag +{ + vec4 color; +}; + +layout(location=0) in VertToFrag vtf; +layout(location=0) out vec4 colorOut; +void main() +{ + colorOut = vtf.color; +} diff --git a/visigen/vulkan/shader.frag.spv b/visigen/vulkan/shader.frag.spv new file mode 100644 index 000000000..9268212a5 Binary files /dev/null and b/visigen/vulkan/shader.frag.spv differ diff --git a/visigen/vulkan/shader.vert b/visigen/vulkan/shader.vert new file mode 100644 index 000000000..86256f8e0 --- /dev/null +++ b/visigen/vulkan/shader.vert @@ -0,0 +1,23 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location=0) in vec4 posIn; +layout(location=1) in vec4 colorIn; + +layout(binding=0) uniform UniformBlock +{ + mat4 projectionMatrix; + mat4 modelViewMatrix; +}; + +struct VertToFrag +{ + vec4 color; +}; + +layout(location=0) out VertToFrag vtf; +void main() +{ + vtf.color = colorIn; + gl_Position = projectionMatrix * modelViewMatrix * vec4(posIn.xyz, 1.0); +} diff --git a/visigen/vulkan/shader.vert.spv b/visigen/vulkan/shader.vert.spv new file mode 100644 index 000000000..56b27ef3d Binary files /dev/null and b/visigen/vulkan/shader.vert.spv differ diff --git a/visigen/vulkan/utils.cpp b/visigen/vulkan/utils.cpp new file mode 100644 index 000000000..7b1434b88 --- /dev/null +++ b/visigen/vulkan/utils.cpp @@ -0,0 +1,767 @@ +// Copyright(c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if defined(_MSC_VER) +// no need to ignore any warnings with MSVC +#elif defined(__clang__) +#pragma clang diagnostic ignored "-Wmissing-braces" +#elif defined(__GNUC__) +// no need to ignore any warnings with GCC +#else +// unknow compiler... just ignore the warnings for yourselves ;) +#endif + +#include "utils.hpp" + +#include + +#include +#include +#include +#include + +#if (VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1) +VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE +#endif + +namespace vk::su { +vk::UniqueDeviceMemory allocateDeviceMemory(vk::Device const& device, + vk::PhysicalDeviceMemoryProperties const& memoryProperties, + vk::MemoryRequirements const& memoryRequirements, + vk::MemoryPropertyFlags memoryPropertyFlags) { + uint32_t memoryTypeIndex = findMemoryType(memoryProperties, memoryRequirements.memoryTypeBits, memoryPropertyFlags); + + return assertSuccess(device.allocateMemoryUnique(vk::MemoryAllocateInfo(memoryRequirements.size, memoryTypeIndex))); +} + +bool contains(std::vector const& extensionProperties, std::string const& extensionName) { + auto propertyIterator = + std::find_if(extensionProperties.begin(), extensionProperties.end(), + [&extensionName](vk::ExtensionProperties const& ep) { return extensionName == ep.extensionName; }); + return (propertyIterator != extensionProperties.end()); +} + +vk::UniqueCommandPool createCommandPool(vk::Device const& device, uint32_t queueFamilyIndex) { + vk::CommandPoolCreateInfo commandPoolCreateInfo(vk::CommandPoolCreateFlagBits::eResetCommandBuffer, queueFamilyIndex); + return assertSuccess(device.createCommandPoolUnique(commandPoolCreateInfo)); +} + +vk::UniqueCommandBuffer createCommandBuffer(vk::Device const& device, vk::CommandPool const& commandPool) { + const vk::CommandBufferAllocateInfo info(commandPool, vk::CommandBufferLevel::ePrimary, 1); + auto commandBuffers = vk::su::assertSuccess(device.allocateCommandBuffersUnique(info)); + return std::move(commandBuffers.front()); +} + +vk::DebugUtilsMessengerEXT createDebugUtilsMessengerEXT(vk::Instance const& instance) { + return instance.createDebugUtilsMessengerEXT(vk::su::makeDebugUtilsMessengerCreateInfoEXT()).value; +} + +vk::UniqueDescriptorPool createDescriptorPool(vk::Device const& device, + std::vector const& poolSizes) { + assert(!poolSizes.empty()); + uint32_t maxSets = + std::accumulate(poolSizes.begin(), poolSizes.end(), 0, + [](uint32_t sum, vk::DescriptorPoolSize const& dps) { return sum + dps.descriptorCount; }); + assert(0 < maxSets); + + vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo(vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet, maxSets, + poolSizes); + return assertSuccess(device.createDescriptorPoolUnique(descriptorPoolCreateInfo)); +} + +vk::UniqueDescriptorSetLayout createDescriptorSetLayout( + vk::Device const& device, + std::vector> const& bindingData, + vk::DescriptorSetLayoutCreateFlags flags) { + std::vector bindings(bindingData.size()); + for (size_t i = 0; i < bindingData.size(); i++) { + bindings[i] = vk::DescriptorSetLayoutBinding(checked_cast(i), std::get<0>(bindingData[i]), + std::get<1>(bindingData[i]), std::get<2>(bindingData[i])); + } + return assertSuccess(device.createDescriptorSetLayoutUnique(vk::DescriptorSetLayoutCreateInfo(flags, bindings))); +} + +vk::UniqueDevice createDevice(vk::PhysicalDevice const& physicalDevice, uint32_t queueFamilyIndex, + std::vector const& extensions, + vk::PhysicalDeviceFeatures const* physicalDeviceFeatures, void const* pNext) { + std::vector enabledExtensions; + enabledExtensions.reserve(extensions.size()); + for (auto const& ext : extensions) { + enabledExtensions.push_back(ext.data()); + } + + float queuePriority = 0.0f; + vk::DeviceQueueCreateInfo deviceQueueCreateInfo({}, queueFamilyIndex, 1, &queuePriority); + vk::DeviceCreateInfo deviceCreateInfo({}, deviceQueueCreateInfo, {}, enabledExtensions, physicalDeviceFeatures); + deviceCreateInfo.pNext = pNext; + + vk::UniqueDevice device = assertSuccess(physicalDevice.createDeviceUnique(deviceCreateInfo)); +#if (VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1) + // initialize function pointers for instance + VULKAN_HPP_DEFAULT_DISPATCHER.init(device.get()); +#endif + return device; +} + +vk::UniqueFramebuffer createFramebuffer(vk::Device const& device, vk::RenderPass& renderPass, + vk::ImageView const& colorImageView, vk::ImageView const& depthImageView, + vk::Extent2D const& extent) { + std::array attachments{colorImageView, depthImageView}; + vk::FramebufferCreateInfo framebufferCreateInfo(vk::FramebufferCreateFlags(), renderPass, depthImageView ? 2 : 1, + attachments.data(), extent.width, extent.height, 1); + return assertSuccess(device.createFramebufferUnique(framebufferCreateInfo)); +} + +vk::UniquePipeline createGraphicsPipeline( + vk::Device const& device, vk::PipelineCache const& pipelineCache, + std::pair const& vertexShaderData, + std::pair const& fragmentShaderData, uint32_t vertexStride, + std::vector> const& vertexInputAttributeFormatOffset, vk::FrontFace frontFace, + bool depthBuffered, vk::PipelineLayout const& pipelineLayout, vk::RenderPass const& renderPass) { + std::array pipelineShaderStageCreateInfos = { + vk::PipelineShaderStageCreateInfo(vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, + vertexShaderData.first, "main", vertexShaderData.second), + vk::PipelineShaderStageCreateInfo(vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, + fragmentShaderData.first, "main", fragmentShaderData.second)}; + + std::vector vertexInputAttributeDescriptions; + vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo; + vk::VertexInputBindingDescription vertexInputBindingDescription(0, vertexStride); + + if (0 < vertexStride) { + vertexInputAttributeDescriptions.reserve(vertexInputAttributeFormatOffset.size()); + for (uint32_t i = 0; i < vertexInputAttributeFormatOffset.size(); i++) { + vertexInputAttributeDescriptions.emplace_back(i, 0, vertexInputAttributeFormatOffset[i].first, + vertexInputAttributeFormatOffset[i].second); + } + pipelineVertexInputStateCreateInfo.setVertexBindingDescriptions(vertexInputBindingDescription); + pipelineVertexInputStateCreateInfo.setVertexAttributeDescriptions(vertexInputAttributeDescriptions); + } + + vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo( + vk::PipelineInputAssemblyStateCreateFlags(), vk::PrimitiveTopology::eTriangleStrip); + + vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(vk::PipelineViewportStateCreateFlags(), 1, + nullptr, 1, nullptr); + + vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo( + vk::PipelineRasterizationStateCreateFlags(), VK_FALSE, VK_FALSE, vk::PolygonMode::eFill, + vk::CullModeFlagBits::eBack, frontFace, VK_FALSE, 0.0f, 0.0f, 0.0f, 1.0f); + + vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo({}, vk::SampleCountFlagBits::e1); + + vk::StencilOpState stencilOpState(vk::StencilOp::eKeep, vk::StencilOp::eKeep, vk::StencilOp::eKeep, + vk::CompareOp::eAlways); + vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo( + vk::PipelineDepthStencilStateCreateFlags(), static_cast(depthBuffered), + static_cast(depthBuffered), vk::CompareOp::eLessOrEqual, VK_FALSE, VK_FALSE, stencilOpState, + stencilOpState); + + vk::ColorComponentFlags colorComponentFlags(vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | + vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA); + vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState( + VK_FALSE, vk::BlendFactor::eZero, vk::BlendFactor::eZero, vk::BlendOp::eAdd, vk::BlendFactor::eZero, + vk::BlendFactor::eZero, vk::BlendOp::eAdd, colorComponentFlags); + vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo( + vk::PipelineColorBlendStateCreateFlags(), VK_FALSE, vk::LogicOp::eNoOp, pipelineColorBlendAttachmentState, + {{1.0f, 1.0f, 1.0f, 1.0f}}); + + std::array dynamicStates = {vk::DynamicState::eViewport, vk::DynamicState::eScissor}; + vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), + dynamicStates); + + vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo( + vk::PipelineCreateFlags(), pipelineShaderStageCreateInfos, &pipelineVertexInputStateCreateInfo, + &pipelineInputAssemblyStateCreateInfo, nullptr, &pipelineViewportStateCreateInfo, + &pipelineRasterizationStateCreateInfo, &pipelineMultisampleStateCreateInfo, &pipelineDepthStencilStateCreateInfo, + &pipelineColorBlendStateCreateInfo, &pipelineDynamicStateCreateInfo, pipelineLayout, renderPass); + + return assertSuccess(device.createGraphicsPipelineUnique(pipelineCache, graphicsPipelineCreateInfo)); +} + +std::vector gatherExtensions(std::vector const& extensions +#if !defined(NDEBUG) + , + std::vector const& extensionProperties +#endif +) { + std::vector enabledExtensions; + enabledExtensions.reserve(extensions.size()); + for (auto const& ext : extensions) { + assert(std::find_if(extensionProperties.begin(), extensionProperties.end(), + [ext](vk::ExtensionProperties const& ep) { return ext == ep.extensionName; }) != + extensionProperties.end()); + enabledExtensions.push_back(ext.data()); + } +#if !defined(NDEBUG) + if (std::find(extensions.begin(), extensions.end(), VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == extensions.end() && + std::find_if(extensionProperties.begin(), extensionProperties.end(), [](vk::ExtensionProperties const& ep) { + return (strcmp(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, ep.extensionName) == 0); + }) != extensionProperties.end()) { + enabledExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } +#endif + return enabledExtensions; +} + +std::vector gatherLayers(std::vector const& layers +#if !defined(NDEBUG) + , + std::vector const& layerProperties +#endif +) { + std::vector enabledLayers; + enabledLayers.reserve(layers.size()); + for (auto const& layer : layers) { + assert(std::find_if(layerProperties.begin(), layerProperties.end(), [layer](vk::LayerProperties const& lp) { + return layer == lp.layerName; + }) != layerProperties.end()); + enabledLayers.push_back(layer.data()); + } +#if !defined(NDEBUG) + // Enable standard validation layer to find as much errors as possible! + if (std::find(layers.begin(), layers.end(), "VK_LAYER_KHRONOS_validation") == layers.end() && + std::find_if(layerProperties.begin(), layerProperties.end(), [](vk::LayerProperties const& lp) { + return (strcmp("VK_LAYER_KHRONOS_validation", lp.layerName) == 0); + }) != layerProperties.end()) { + enabledLayers.push_back("VK_LAYER_KHRONOS_validation"); + } +#endif + return enabledLayers; +} + +vk::UniqueInstance createInstance(std::string const& appName, std::string const& engineName, + std::vector const& layers, std::vector const& extensions, + uint32_t apiVersion) { +#if (VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1) + static vk::DynamicLoader dl; + auto vkGetInstanceProcAddr = dl.getProcAddress("vkGetInstanceProcAddr"); + VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr); +#endif + + vk::ApplicationInfo applicationInfo(appName.c_str(), 1, engineName.c_str(), 1, apiVersion); + std::vector enabledLayers = vk::su::gatherLayers(layers +#if !defined(NDEBUG) + , vk::enumerateInstanceLayerProperties().value +#endif + ); + std::vector enabledExtensions = + vk::su::gatherExtensions(extensions +#if !defined(NDEBUG) + , vk::enumerateInstanceExtensionProperties().value +#endif + ); + + vk::UniqueInstance instance = assertSuccess(vk::createInstanceUnique( + makeInstanceCreateInfoChain(applicationInfo, enabledLayers, enabledExtensions).get())); + +#if (VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1) + // initialize function pointers for instance + VULKAN_HPP_DEFAULT_DISPATCHER.init(instance.get()); +#endif + + return instance; +} + +vk::UniqueRenderPass createRenderPass(vk::Device const& device, vk::Format colorFormat, vk::Format depthFormat, + vk::AttachmentLoadOp colorLoadOp, vk::AttachmentStoreOp colorStoreOp, + vk::AttachmentLoadOp depthLoadOp, vk::AttachmentStoreOp depthStoreOp, + vk::ImageLayout colorFinalLayout) { + std::vector attachmentDescriptions; + assert(colorFormat != vk::Format::eUndefined); + attachmentDescriptions.emplace_back(vk::AttachmentDescriptionFlags(), colorFormat, vk::SampleCountFlagBits::e1, + colorLoadOp, colorStoreOp, vk::AttachmentLoadOp::eDontCare, + vk::AttachmentStoreOp::eDontCare, vk::ImageLayout::eUndefined, colorFinalLayout); + if (depthFormat != vk::Format::eUndefined) { + attachmentDescriptions.emplace_back( + vk::AttachmentDescriptionFlags(), depthFormat, vk::SampleCountFlagBits::e1, depthLoadOp, depthStoreOp, + vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare, + vk::ImageLayout::eUndefined, vk::ImageLayout::eDepthStencilAttachmentOptimal); + } + vk::AttachmentReference colorAttachment(0, vk::ImageLayout::eColorAttachmentOptimal); + vk::AttachmentReference depthAttachment(1, vk::ImageLayout::eDepthStencilAttachmentOptimal); + vk::SubpassDescription subpassDescription(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, {}, + colorAttachment, {}, + (depthFormat != vk::Format::eUndefined) ? &depthAttachment : nullptr); + return assertSuccess(device.createRenderPassUnique( + vk::RenderPassCreateInfo(vk::RenderPassCreateFlags(), attachmentDescriptions, subpassDescription))); +} + +VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsMessengerCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageTypes, + VkDebugUtilsMessengerCallbackDataEXT const* pCallbackData, + void* /*pUserData*/) { +#if !defined(NDEBUG) + if (pCallbackData->messageIdNumber == 648835635) { + // UNASSIGNED-khronos-Validation-debug-build-warning-message + return VK_FALSE; + } + if (pCallbackData->messageIdNumber == 767975156) { + // UNASSIGNED-BestPractices-vkCreateInstance-specialuse-extension + return VK_FALSE; + } +#endif + + std::cerr << vk::to_string(static_cast(messageSeverity)) << ": " + << vk::to_string(static_cast(messageTypes)) << ":\n"; + std::cerr << "\t" + << "messageIDName = <" << pCallbackData->pMessageIdName << ">\n"; + std::cerr << "\t" + << "messageIdNumber = " << pCallbackData->messageIdNumber << "\n"; + std::cerr << "\t" + << "message = <" << pCallbackData->pMessage << ">\n"; + if (0 < pCallbackData->queueLabelCount) { + std::cerr << "\t" + << "Queue Labels:\n"; + for (uint8_t i = 0; i < pCallbackData->queueLabelCount; i++) { + std::cerr << "\t\t" + << "labelName = <" << pCallbackData->pQueueLabels[i].pLabelName << ">\n"; + } + } + if (0 < pCallbackData->cmdBufLabelCount) { + std::cerr << "\t" + << "CommandBuffer Labels:\n"; + for (uint8_t i = 0; i < pCallbackData->cmdBufLabelCount; i++) { + std::cerr << "\t\t" + << "labelName = <" << pCallbackData->pCmdBufLabels[i].pLabelName << ">\n"; + } + } + if (0 < pCallbackData->objectCount) { + std::cerr << "\t" + << "Objects:\n"; + for (uint8_t i = 0; i < pCallbackData->objectCount; i++) { + std::cerr << "\t\t" + << "Object " << i << "\n"; + std::cerr << "\t\t\t" + << "objectType = " + << vk::to_string(static_cast(pCallbackData->pObjects[i].objectType)) << "\n"; + std::cerr << "\t\t\t" + << "objectHandle = " << pCallbackData->pObjects[i].objectHandle << "\n"; + if (pCallbackData->pObjects[i].pObjectName != nullptr) { + std::cerr << "\t\t\t" + << "objectName = <" << pCallbackData->pObjects[i].pObjectName << ">\n"; + } + } + } + return VK_TRUE; +} + +uint32_t findGraphicsQueueFamilyIndex(std::vector const& queueFamilyProperties) { + // get the first index into queueFamiliyProperties which supports graphics + const auto graphicsQueueFamilyProperty = + std::find_if(queueFamilyProperties.begin(), queueFamilyProperties.end(), + [](vk::QueueFamilyProperties const& qfp) { return qfp.queueFlags & vk::QueueFlagBits::eGraphics; }); + assert(graphicsQueueFamilyProperty != queueFamilyProperties.end()); + return static_cast(std::distance(queueFamilyProperties.begin(), graphicsQueueFamilyProperty)); +} + +std::pair findGraphicsAndPresentQueueFamilyIndex(vk::PhysicalDevice physicalDevice, + vk::SurfaceKHR const& surface) { + std::vector queueFamilyProperties = physicalDevice.getQueueFamilyProperties(); + assert(queueFamilyProperties.size() < std::numeric_limits::max()); + + uint32_t graphicsQueueFamilyIndex = findGraphicsQueueFamilyIndex(queueFamilyProperties); + if (physicalDevice.getSurfaceSupportKHR(graphicsQueueFamilyIndex, surface).value == VK_TRUE) { + return std::make_pair(graphicsQueueFamilyIndex, + graphicsQueueFamilyIndex); // the first graphicsQueueFamilyIndex does also support presents + } + + // the graphicsQueueFamilyIndex doesn't support present -> look for an other family index that supports both + // graphics and present + for (size_t i = 0; i < queueFamilyProperties.size(); i++) { + if ((queueFamilyProperties[i].queueFlags & vk::QueueFlagBits::eGraphics) && + physicalDevice.getSurfaceSupportKHR(static_cast(i), surface).value == VK_TRUE) { + return std::make_pair(static_cast(i), static_cast(i)); + } + } + + // there's nothing like a single family index that supports both graphics and present -> look for an other family + // index that supports present + for (size_t i = 0; i < queueFamilyProperties.size(); i++) { + if (physicalDevice.getSurfaceSupportKHR(static_cast(i), surface).value == VK_TRUE) { + return std::make_pair(graphicsQueueFamilyIndex, static_cast(i)); + } + } + + assert(false && "Could not find queues for both graphics or present -> terminating"); + return {}; +} + +uint32_t findMemoryType(vk::PhysicalDeviceMemoryProperties const& memoryProperties, uint32_t typeBits, + vk::MemoryPropertyFlags requirementsMask) { + auto typeIndex = uint32_t(~0); + for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) { + if (((typeBits & 1) != 0u) && + ((memoryProperties.memoryTypes[i].propertyFlags & requirementsMask) == requirementsMask)) { + typeIndex = i; + break; + } + typeBits >>= 1; + } + assert(typeIndex != uint32_t(~0)); + return typeIndex; +} + +std::vector getDeviceExtensions() { return {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; } + +std::vector getInstanceExtensions() { + std::vector extensions; + extensions.emplace_back(VK_KHR_SURFACE_EXTENSION_NAME); +#if defined(VK_USE_PLATFORM_ANDROID_KHR) + extensions.emplace_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME); +#elif defined(VK_USE_PLATFORM_IOS_MVK) + extensions.emplace_back(VK_MVK_IOS_SURFACE_EXTENSION_NAME); +#elif defined(VK_USE_PLATFORM_MACOS_MVK) + extensions.emplace_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME); +#elif defined(VK_USE_PLATFORM_MIR_KHR) + extensions.emplace_back(VK_KHR_MIR_SURFACE_EXTENSION_NAME); +#elif defined(VK_USE_PLATFORM_VI_NN) + extensions.emplace_back(VK_NN_VI_SURFACE_EXTENSION_NAME); +#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) + extensions.emplace_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); +#elif defined(VK_USE_PLATFORM_WIN32_KHR) + extensions.emplace_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); +#elif defined(VK_USE_PLATFORM_XCB_KHR) + extensions.emplace_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME); +#elif defined(VK_USE_PLATFORM_XLIB_KHR) + extensions.emplace_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); +#elif defined(VK_USE_PLATFORM_XLIB_XRANDR_EXT) + extensions.emplace_back(VK_EXT_ACQUIRE_XLIB_DISPLAY_EXTENSION_NAME); +#endif + return extensions; +} + +vk::Format pickDepthFormat(vk::PhysicalDevice const& physicalDevice) { + std::vector candidates = {vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint, + vk::Format::eD24UnormS8Uint}; + for (vk::Format format : candidates) { + vk::FormatProperties props = physicalDevice.getFormatProperties(format); + + if (props.optimalTilingFeatures & vk::FormatFeatureFlagBits::eDepthStencilAttachment) { + return format; + } + } + return vk::Format::eUndefined; +} + +vk::PresentModeKHR pickPresentMode(std::vector const& presentModes) { + vk::PresentModeKHR pickedMode = vk::PresentModeKHR::eFifo; + for (const auto& presentMode : presentModes) { + if (presentMode == vk::PresentModeKHR::eMailbox) { + pickedMode = presentMode; + break; + } + + if (presentMode == vk::PresentModeKHR::eImmediate) { + pickedMode = presentMode; + } + } + return pickedMode; +} + +vk::SurfaceFormatKHR pickSurfaceFormat(std::vector const& formats) { + assert(!formats.empty()); + vk::SurfaceFormatKHR pickedFormat = formats[0]; + if (formats.size() == 1) { + if (formats[0].format == vk::Format::eUndefined) { + pickedFormat.format = vk::Format::eB8G8R8A8Unorm; + pickedFormat.colorSpace = vk::ColorSpaceKHR::eSrgbNonlinear; + } + } else { + // request several formats, the first found will be used + vk::Format requestedFormats[] = {vk::Format::eB8G8R8A8Unorm, vk::Format::eR8G8B8A8Unorm, vk::Format::eB8G8R8Unorm, + vk::Format::eR8G8B8Unorm}; + vk::ColorSpaceKHR requestedColorSpace = vk::ColorSpaceKHR::eSrgbNonlinear; + for (size_t i = 0; i < sizeof(requestedFormats) / sizeof(requestedFormats[0]); i++) { + vk::Format requestedFormat = requestedFormats[i]; + auto it = std::find_if(formats.begin(), formats.end(), + [requestedFormat, requestedColorSpace](vk::SurfaceFormatKHR const& f) { + return (f.format == requestedFormat) && (f.colorSpace == requestedColorSpace); + }); + if (it != formats.end()) { + pickedFormat = *it; + break; + } + } + } + assert(pickedFormat.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear); + return pickedFormat; +} + +void setImageLayout(vk::CommandBuffer const& commandBuffer, vk::Image image, vk::Format format, + vk::ImageLayout oldImageLayout, vk::ImageLayout newImageLayout) { + vk::AccessFlags sourceAccessMask; + switch (oldImageLayout) { + case vk::ImageLayout::eTransferDstOptimal: + sourceAccessMask = vk::AccessFlagBits::eTransferWrite; + break; + case vk::ImageLayout::ePreinitialized: + sourceAccessMask = vk::AccessFlagBits::eHostWrite; + break; + case vk::ImageLayout::eGeneral: // sourceAccessMask is empty + case vk::ImageLayout::eUndefined: + break; + default: + assert(false); + break; + } + + vk::PipelineStageFlags sourceStage; + switch (oldImageLayout) { + case vk::ImageLayout::eGeneral: + case vk::ImageLayout::ePreinitialized: + sourceStage = vk::PipelineStageFlagBits::eHost; + break; + case vk::ImageLayout::eTransferDstOptimal: + sourceStage = vk::PipelineStageFlagBits::eTransfer; + break; + case vk::ImageLayout::eUndefined: + sourceStage = vk::PipelineStageFlagBits::eTopOfPipe; + break; + default: + assert(false); + break; + } + + vk::AccessFlags destinationAccessMask; + switch (newImageLayout) { + case vk::ImageLayout::eColorAttachmentOptimal: + destinationAccessMask = vk::AccessFlagBits::eColorAttachmentWrite; + break; + case vk::ImageLayout::eDepthStencilAttachmentOptimal: + destinationAccessMask = + vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite; + break; + case vk::ImageLayout::eGeneral: // empty destinationAccessMask + case vk::ImageLayout::ePresentSrcKHR: + break; + case vk::ImageLayout::eShaderReadOnlyOptimal: + destinationAccessMask = vk::AccessFlagBits::eShaderRead; + break; + case vk::ImageLayout::eTransferSrcOptimal: + destinationAccessMask = vk::AccessFlagBits::eTransferRead; + break; + case vk::ImageLayout::eTransferDstOptimal: + destinationAccessMask = vk::AccessFlagBits::eTransferWrite; + break; + default: + assert(false); + break; + } + + vk::PipelineStageFlags destinationStage; + switch (newImageLayout) { + case vk::ImageLayout::eColorAttachmentOptimal: + destinationStage = vk::PipelineStageFlagBits::eColorAttachmentOutput; + break; + case vk::ImageLayout::eDepthStencilAttachmentOptimal: + destinationStage = vk::PipelineStageFlagBits::eEarlyFragmentTests; + break; + case vk::ImageLayout::eGeneral: + destinationStage = vk::PipelineStageFlagBits::eHost; + break; + case vk::ImageLayout::ePresentSrcKHR: + destinationStage = vk::PipelineStageFlagBits::eBottomOfPipe; + break; + case vk::ImageLayout::eShaderReadOnlyOptimal: + destinationStage = vk::PipelineStageFlagBits::eFragmentShader; + break; + case vk::ImageLayout::eTransferDstOptimal: + case vk::ImageLayout::eTransferSrcOptimal: + destinationStage = vk::PipelineStageFlagBits::eTransfer; + break; + default: + assert(false); + break; + } + + vk::ImageAspectFlags aspectMask; + if (newImageLayout == vk::ImageLayout::eDepthStencilAttachmentOptimal) { + aspectMask = vk::ImageAspectFlagBits::eDepth; + if (format == vk::Format::eD32SfloatS8Uint || format == vk::Format::eD24UnormS8Uint) { + aspectMask |= vk::ImageAspectFlagBits::eStencil; + } + } else { + aspectMask = vk::ImageAspectFlagBits::eColor; + } + + vk::ImageSubresourceRange imageSubresourceRange(aspectMask, 0, 1, 0, 1); + vk::ImageMemoryBarrier imageMemoryBarrier(sourceAccessMask, destinationAccessMask, oldImageLayout, newImageLayout, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, image, + imageSubresourceRange); + return commandBuffer.pipelineBarrier(sourceStage, destinationStage, {}, nullptr, nullptr, imageMemoryBarrier); +} + +void submitAndWait(vk::Device const& device, vk::Queue const& queue, vk::CommandBuffer const& commandBuffer) { + vk::Fence fence = device.createFence(vk::FenceCreateInfo()).value; + queue.submit(vk::SubmitInfo(0, nullptr, nullptr, 1, &commandBuffer), fence); + while (vk::Result::eTimeout == device.waitForFences(fence, VK_TRUE, vk::su::FenceTimeout)) {} + device.destroyFence(fence); +} + +void updateDescriptorSets( + vk::Device const& device, vk::DescriptorSet const& descriptorSet, + std::vector> const& bufferData, + uint32_t bindingOffset) { + std::vector bufferInfos; + bufferInfos.reserve(bufferData.size()); + + std::vector writeDescriptorSets; + writeDescriptorSets.reserve(bufferData.size() + 1); + uint32_t dstBinding = bindingOffset; + for (auto const& bd : bufferData) { + bufferInfos.emplace_back(std::get<1>(bd), 0, std::get<2>(bd)); + writeDescriptorSets.emplace_back(descriptorSet, dstBinding++, 0, 1, std::get<0>(bd), nullptr, &bufferInfos.back()); + } + + device.updateDescriptorSets(writeDescriptorSets, nullptr); +} + +BufferData::BufferData(vk::PhysicalDevice const& physicalDevice, vk::Device const& device, vk::DeviceSize size, + vk::BufferUsageFlags usage, vk::MemoryPropertyFlags propertyFlags) +: size(size) +#if !defined(NDEBUG) +, m_usage(usage) +, m_propertyFlags(propertyFlags) +#endif +{ + buffer = assertSuccess(device.createBufferUnique(vk::BufferCreateInfo(vk::BufferCreateFlags(), size, usage))); + deviceMemory = vk::su::allocateDeviceMemory(device, physicalDevice.getMemoryProperties(), + device.getBufferMemoryRequirements(buffer.get()), propertyFlags); + device.bindBufferMemory(buffer.get(), deviceMemory.get(), 0); +} + +DepthBufferData::DepthBufferData(vk::PhysicalDevice const& physicalDevice, vk::Device const& device, vk::Format format, + vk::Extent2D const& extent) +: ImageData(physicalDevice, device, format, extent, vk::ImageTiling::eOptimal, + vk::ImageUsageFlagBits::eDepthStencilAttachment, vk::ImageLayout::eUndefined, + vk::MemoryPropertyFlagBits::eDeviceLocal, vk::ImageAspectFlagBits::eDepth) {} + +ImageData::ImageData(vk::PhysicalDevice const& physicalDevice, vk::Device const& device, vk::Format format_, + vk::Extent2D const& extent, vk::ImageTiling tiling, vk::ImageUsageFlags usage, + vk::ImageLayout initialLayout, vk::MemoryPropertyFlags memoryProperties, + vk::ImageAspectFlags aspectMask) +: format(format_) { + vk::ImageCreateInfo imageCreateInfo(vk::ImageCreateFlags(), vk::ImageType::e2D, format, vk::Extent3D(extent, 1), 1, 1, + vk::SampleCountFlagBits::e1, tiling, usage | vk::ImageUsageFlagBits::eSampled, + vk::SharingMode::eExclusive, {}, initialLayout); + image = assertSuccess(device.createImageUnique(imageCreateInfo)); + + auto memoryRequirements = device.getImageMemoryRequirements(image.get()); + deviceSize = memoryRequirements.size; + deviceMemory = vk::su::allocateDeviceMemory(device, physicalDevice.getMemoryProperties(), memoryRequirements, memoryProperties); + + device.bindImageMemory(image.get(), deviceMemory.get(), 0); + + vk::ComponentMapping componentMapping(ComponentSwizzle::eR, ComponentSwizzle::eG, ComponentSwizzle::eB, + ComponentSwizzle::eA); + vk::ImageSubresourceRange imageSubresourceRange(aspectMask, 0, 1, 0, 1); + vk::ImageViewCreateInfo imageViewCreateInfo({}, image.get(), vk::ImageViewType::e2D, format, componentMapping, + imageSubresourceRange); + imageView = assertSuccess(device.createImageViewUnique(imageViewCreateInfo)); +} + +TextureData::TextureData(vk::PhysicalDevice const& physicalDevice, vk::Device const& device, + vk::Extent2D const& extent_, vk::ImageUsageFlags usageFlags, + vk::FormatFeatureFlags formatFeatureFlags, bool anisotropyEnable, bool forceStaging) +: format(vk::Format::eR8G8B8A8Unorm), extent(extent_) { + vk::FormatProperties formatProperties = physicalDevice.getFormatProperties(format); + + formatFeatureFlags |= vk::FormatFeatureFlagBits::eSampledImage; + needsStaging = forceStaging || ((formatProperties.linearTilingFeatures & formatFeatureFlags) != formatFeatureFlags); + vk::ImageTiling imageTiling; + vk::ImageLayout initialLayout; + vk::MemoryPropertyFlags requirements; + if (needsStaging) { + assert((formatProperties.optimalTilingFeatures & formatFeatureFlags) == formatFeatureFlags); + stagingBufferData = std::make_unique(physicalDevice, device, extent.width * extent.height * 4, + vk::BufferUsageFlagBits::eTransferSrc); + imageTiling = vk::ImageTiling::eOptimal; + usageFlags |= vk::ImageUsageFlagBits::eTransferDst; + initialLayout = vk::ImageLayout::eUndefined; + } else { + imageTiling = vk::ImageTiling::eLinear; + initialLayout = vk::ImageLayout::ePreinitialized; + requirements = vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eHostVisible; + } + imageData = std::make_unique(physicalDevice, device, format, extent, imageTiling, + usageFlags | vk::ImageUsageFlagBits::eSampled, initialLayout, requirements, + vk::ImageAspectFlagBits::eColor); + + sampler = assertSuccess(device.createSamplerUnique(vk::SamplerCreateInfo( + vk::SamplerCreateFlags(), vk::Filter::eLinear, vk::Filter::eLinear, vk::SamplerMipmapMode::eLinear, + vk::SamplerAddressMode::eRepeat, vk::SamplerAddressMode::eRepeat, vk::SamplerAddressMode::eRepeat, 0.0f, + static_cast(anisotropyEnable), 16.0f, VK_FALSE, vk::CompareOp::eNever, 0.0f, 0.0f, + vk::BorderColor::eFloatOpaqueBlack))); +} + +UUID::UUID(uint8_t const data[VK_UUID_SIZE]) { memcpy(m_data, data, VK_UUID_SIZE * sizeof(uint8_t)); } + +vk::DebugUtilsMessengerCreateInfoEXT makeDebugUtilsMessengerCreateInfoEXT() { + return {{}, + vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError, + vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance | + vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation, + &vk::su::debugUtilsMessengerCallback}; +} + +#if defined(NDEBUG) +vk::StructureChain +#elif defined(VULKAN_HPP_UTILS_USE_BEST_PRACTICES) +vk::StructureChain +#else +vk::StructureChain +#endif +makeInstanceCreateInfoChain(vk::ApplicationInfo const& applicationInfo, std::vector const& enabledLayers, + std::vector const& enabledExtensions) { +#if defined(NDEBUG) + // in non-debug mode just use the InstanceCreateInfo for instance creation + vk::StructureChain instanceCreateInfo( + {{}, &applicationInfo, enabledLayers, enabledExtensions}); +#else + // in debug mode, addionally use the debugUtilsMessengerCallback in instance creation! + vk::DebugUtilsMessageSeverityFlagsEXT severityFlags(vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | + vk::DebugUtilsMessageSeverityFlagBitsEXT::eError); + vk::DebugUtilsMessageTypeFlagsEXT messageTypeFlags(vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | + vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance | + vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation); +#if defined(VULKAN_HPP_UTILS_USE_BEST_PRACTICES) + vk::ValidationFeatureEnableEXT validationFeatureEnable = vk::ValidationFeatureEnableEXT::eBestPractices; + vk::StructureChain + instanceCreateInfo({{}, &applicationInfo, enabledLayers, enabledExtensions}, + {{}, severityFlags, messageTypeFlags, &vk::su::debugUtilsMessengerCallback}, + {validationFeatureEnable}); +#else + vk::StructureChain instanceCreateInfo( + {{}, &applicationInfo, enabledLayers, enabledExtensions}, + {{}, severityFlags, messageTypeFlags, &vk::su::debugUtilsMessengerCallback}); +#endif +#endif + return instanceCreateInfo; +} + +} // namespace vk::su + +std::ostream& operator<<(std::ostream& os, vk::su::UUID const& uuid) { + os << std::setfill('0') << std::hex; + for (uint32_t j = 0; j < VK_UUID_SIZE; ++j) { + os << std::setw(2) << static_cast(uuid.m_data[j]); + if (j == 3 || j == 5 || j == 7 || j == 9) { + std::cout << '-'; + } + } + os << std::setfill(' ') << std::dec; + return os; +} diff --git a/visigen/vulkan/utils.hpp b/visigen/vulkan/utils.hpp new file mode 100644 index 000000000..8224f1070 --- /dev/null +++ b/visigen/vulkan/utils.hpp @@ -0,0 +1,303 @@ +#pragma once + +// Copyright(c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#define VULKAN_HPP_NO_EXCEPTIONS 1 +#define VULKAN_HPP_ASSERT_ON_RESULT +//#define VULKAN_HPP_UTILS_USE_BEST_PRACTICES 1 +#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1 + +#include + +#include +#include + +namespace vk::su { +template +inline static T assertSuccess(vk::ResultValue result) { + assert(result.result == vk::Result::eSuccess); + return std::move(result.value); +} + +const uint64_t FenceTimeout = 100000000; + +template +void oneTimeSubmit(vk::CommandBuffer const& commandBuffer, vk::Queue const& queue, Func const& func) { + commandBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit)); + func(commandBuffer); + commandBuffer.end(); + queue.submit(vk::SubmitInfo(0, nullptr, nullptr, 1, &commandBuffer), nullptr); + queue.waitIdle(); +} + +template +void oneTimeSubmit(vk::Device const& device, vk::CommandPool const& commandPool, vk::Queue const& queue, + Func const& func) { + vk::CommandBuffer commandBuffer = + device.allocateCommandBuffers(vk::CommandBufferAllocateInfo(commandPool, vk::CommandBufferLevel::ePrimary, 1)) + .value.front(); + oneTimeSubmit(commandBuffer, queue, func); +} + +template +void copyToDevice(vk::Device const& device, vk::DeviceMemory const& deviceMemory, T const* pData, size_t count, + vk::DeviceSize stride = sizeof(T)) { + assert(sizeof(T) <= stride); + uint8_t* deviceData = static_cast(device.mapMemory(deviceMemory, 0, count * stride).value); + if (stride == sizeof(T)) { + memcpy(deviceData, pData, count * sizeof(T)); + } else { + for (size_t i = 0; i < count; i++) { + memcpy(deviceData, &pData[i], sizeof(T)); + deviceData += stride; + } + } + device.unmapMemory(deviceMemory); +} + +template +void copyToDevice(vk::Device const& device, vk::DeviceMemory const& deviceMemory, T const& data) { + copyToDevice(device, deviceMemory, &data, 1); +} + +template +VULKAN_HPP_INLINE constexpr const T& clamp(const T& v, const T& lo, const T& hi) { + return v < lo ? lo : hi < v ? hi : v; +} + +void setImageLayout(vk::CommandBuffer const& commandBuffer, vk::Image image, vk::Format format, + vk::ImageLayout oldImageLayout, vk::ImageLayout newImageLayout); + +struct BufferData { + BufferData(vk::PhysicalDevice const& physicalDevice, vk::Device const& device, vk::DeviceSize size, + vk::BufferUsageFlags usage, + vk::MemoryPropertyFlags propertyFlags = vk::MemoryPropertyFlagBits::eHostVisible | + vk::MemoryPropertyFlagBits::eHostCoherent); + + template + void upload(vk::Device const& device, DataType const& data) const { + assert((m_propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent) && + (m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible)); + assert(sizeof(DataType) <= size); + + void* dataPtr = assertSuccess(device.mapMemory(deviceMemory.get(), 0, sizeof(DataType))); + memcpy(dataPtr, &data, sizeof(DataType)); + device.unmapMemory(deviceMemory.get()); + } + + template + void upload(vk::Device const& device, std::vector const& data, size_t stride = 0) const { + assert(m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible); + + size_t elementSize = stride ? stride : sizeof(DataType); + assert(sizeof(DataType) <= elementSize); + + copyToDevice(device, deviceMemory, data.data(), data.size(), elementSize); + } + + template + void upload(vk::PhysicalDevice const& physicalDevice, vk::Device const& device, vk::CommandPool const& commandPool, + vk::Queue queue, std::vector const& data, size_t stride) const { + assert(m_usage & vk::BufferUsageFlagBits::eTransferDst); + assert(m_propertyFlags & vk::MemoryPropertyFlagBits::eDeviceLocal); + + size_t elementSize = stride ? stride : sizeof(DataType); + assert(sizeof(DataType) <= elementSize); + + size_t dataSize = data.size() * elementSize; + assert(dataSize <= size); + + vk::su::BufferData stagingBuffer(physicalDevice, device, dataSize, vk::BufferUsageFlagBits::eTransferSrc); + copyToDevice(device, stagingBuffer.deviceMemory, data.data(), data.size(), elementSize); + + vk::su::oneTimeSubmit(device, commandPool, queue, [&](vk::CommandBuffer const& commandBuffer) { + commandBuffer.copyBuffer(stagingBuffer.buffer.get(), buffer.get(), vk::BufferCopy(0, 0, dataSize)); + }); + } + + void* map(vk::Device const& device) { return assertSuccess(device.mapMemory(deviceMemory.get(), 0, size)); } + + void unmap(vk::Device const& device) { device.unmapMemory(deviceMemory.get()); } + + vk::UniqueBuffer buffer; + vk::UniqueDeviceMemory deviceMemory; + vk::DeviceSize size; +#if !defined(NDEBUG) +private: + vk::BufferUsageFlags m_usage; + vk::MemoryPropertyFlags m_propertyFlags; +#endif +}; + +struct ImageData { + ImageData(vk::PhysicalDevice const& physicalDevice, vk::Device const& device, vk::Format format, + vk::Extent2D const& extent, vk::ImageTiling tiling, vk::ImageUsageFlags usage, + vk::ImageLayout initialLayout, vk::MemoryPropertyFlags memoryProperties, vk::ImageAspectFlags aspectMask); + + vk::Format format; + vk::UniqueImage image; + vk::UniqueDeviceMemory deviceMemory; + vk::DeviceSize deviceSize; + vk::UniqueImageView imageView; +}; + +struct DepthBufferData : public ImageData { + DepthBufferData(vk::PhysicalDevice const& physicalDevice, vk::Device const& device, vk::Format format, + vk::Extent2D const& extent); +}; + +struct TextureData { + TextureData(vk::PhysicalDevice const& physicalDevice, vk::Device const& device, + vk::Extent2D const& extent_ = {256, 256}, vk::ImageUsageFlags usageFlags = {}, + vk::FormatFeatureFlags formatFeatureFlags = {}, bool anisotropyEnable = false, bool forceStaging = false); + + template + void setImage(vk::Device const& device, vk::CommandBuffer const& commandBuffer, + ImageGenerator const& imageGenerator) { + void* data = + needsStaging + ? assertSuccess(device.mapMemory(stagingBufferData->deviceMemory.get(), 0, stagingBufferData->size)) + : assertSuccess(device.mapMemory(imageData->deviceMemory.get(), 0, imageData->deviceSize)); + imageGenerator(data, extent); + device.unmapMemory(needsStaging ? stagingBufferData->deviceMemory.get() : imageData->deviceMemory.get()); + + if (needsStaging) { + // Since we're going to blit to the texture image, set its layout to eTransferDstOptimal + vk::su::setImageLayout(commandBuffer, imageData->image.get(), imageData->format, vk::ImageLayout::eUndefined, + vk::ImageLayout::eTransferDstOptimal); + vk::BufferImageCopy copyRegion(0, extent.width, extent.height, + vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1), + vk::Offset3D(0, 0, 0), vk::Extent3D(extent, 1)); + commandBuffer.copyBufferToImage(stagingBufferData->buffer.get(), imageData->image.get(), + vk::ImageLayout::eTransferDstOptimal, copyRegion); + // Set the layout for the texture image from eTransferDstOptimal to SHADER_READ_ONLY + vk::su::setImageLayout(commandBuffer, imageData->image.get(), imageData->format, + vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal); + } else { + // If we can use the linear tiled image as a texture, just do it + vk::su::setImageLayout(commandBuffer, imageData->image.get(), imageData->format, vk::ImageLayout::ePreinitialized, + vk::ImageLayout::eShaderReadOnlyOptimal); + } + } + + vk::Format format; + vk::Extent2D extent; + bool needsStaging; + std::unique_ptr stagingBufferData; + std::unique_ptr imageData; + vk::UniqueSampler sampler; +}; + +struct UUID { +public: + UUID(uint8_t const data[VK_UUID_SIZE]); + + uint8_t m_data[VK_UUID_SIZE]; +}; + +template +VULKAN_HPP_INLINE TargetType checked_cast(SourceType value) { + static_assert(sizeof(TargetType) <= sizeof(SourceType), "No need to cast from smaller to larger type!"); + static_assert(std::numeric_limits::is_integer, "Only integer types supported!"); + static_assert(!std::numeric_limits::is_signed, "Only unsigned types supported!"); + static_assert(std::numeric_limits::is_integer, "Only integer types supported!"); + static_assert(!std::numeric_limits::is_signed, "Only unsigned types supported!"); + assert(value <= std::numeric_limits::max()); + return static_cast(value); +} + +vk::UniqueDeviceMemory allocateDeviceMemory(vk::Device const& device, + vk::PhysicalDeviceMemoryProperties const& memoryProperties, + vk::MemoryRequirements const& memoryRequirements, + vk::MemoryPropertyFlags memoryPropertyFlags); +bool contains(std::vector const& extensionProperties, std::string const& extensionName); +vk::UniqueCommandPool createCommandPool(vk::Device const& device, uint32_t queueFamilyIndex); +vk::UniqueCommandBuffer createCommandBuffer(vk::Device const& device, vk::CommandPool const& commandPool); +vk::DebugUtilsMessengerEXT createDebugUtilsMessengerEXT(vk::Instance const& instance); +vk::UniqueDescriptorPool createDescriptorPool(vk::Device const& device, + std::vector const& poolSizes); +vk::UniqueDescriptorSetLayout createDescriptorSetLayout( + vk::Device const& device, + std::vector> const& bindingData, + vk::DescriptorSetLayoutCreateFlags flags = {}); +vk::UniqueDevice createDevice(vk::PhysicalDevice const& physicalDevice, uint32_t queueFamilyIndex, + std::vector const& extensions = {}, + vk::PhysicalDeviceFeatures const* physicalDeviceFeatures = nullptr, + void const* pNext = nullptr); +vk::UniqueFramebuffer createFramebuffer(vk::Device const& device, vk::RenderPass& renderPass, + vk::ImageView const& colorImageView, vk::ImageView const& depthImageView, + vk::Extent2D const& extent); +vk::UniquePipeline createGraphicsPipeline( + vk::Device const& device, vk::PipelineCache const& pipelineCache, + std::pair const& vertexShaderData, + std::pair const& fragmentShaderData, uint32_t vertexStride, + std::vector> const& vertexInputAttributeFormatOffset, vk::FrontFace frontFace, + bool depthBuffered, vk::PipelineLayout const& pipelineLayout, vk::RenderPass const& renderPass); +vk::UniqueInstance createInstance(std::string const& appName, std::string const& engineName, + std::vector const& layers = {}, + std::vector const& extensions = {}, + uint32_t apiVersion = VK_API_VERSION_1_0); +vk::UniqueRenderPass createRenderPass(vk::Device const& device, vk::Format colorFormat, vk::Format depthFormat, + vk::AttachmentLoadOp colorloadOp = vk::AttachmentLoadOp::eDontCare, + vk::AttachmentStoreOp colorStoreOp = vk::AttachmentStoreOp::eStore, + vk::AttachmentLoadOp depthLoadOp = vk::AttachmentLoadOp::eClear, + vk::AttachmentStoreOp depthStoreOp = vk::AttachmentStoreOp::eDontCare, + vk::ImageLayout colorFinalLayout = vk::ImageLayout::ePresentSrcKHR); +VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsMessengerCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageTypes, + VkDebugUtilsMessengerCallbackDataEXT const* pCallbackData, + void* /*pUserData*/); +uint32_t findGraphicsQueueFamilyIndex(std::vector const& queueFamilyProperties); +std::pair findGraphicsAndPresentQueueFamilyIndex(vk::PhysicalDevice physicalDevice, + vk::SurfaceKHR const& surface); +uint32_t findMemoryType(vk::PhysicalDeviceMemoryProperties const& memoryProperties, uint32_t typeBits, + vk::MemoryPropertyFlags requirementsMask); +std::vector gatherExtensions(std::vector const& extensions +#if !defined(NDEBUG) + , + std::vector const& extensionProperties +#endif +); +std::vector gatherLayers(std::vector const& layers +#if !defined(NDEBUG) + , + std::vector const& layerProperties +#endif +); +std::vector getDeviceExtensions(); +std::vector getInstanceExtensions(); +vk::DebugUtilsMessengerCreateInfoEXT makeDebugUtilsMessengerCreateInfoEXT(); +#if defined(NDEBUG) +vk::StructureChain +#elif defined(VULKAN_HPP_UTILS_USE_BEST_PRACTICES) +vk::StructureChain +#else +vk::StructureChain +#endif +makeInstanceCreateInfoChain(vk::ApplicationInfo const& applicationInfo, std::vector const& enabledLayers, + std::vector const& enabledExtensions); +vk::Format pickDepthFormat(vk::PhysicalDevice const& physicalDevice); +vk::PresentModeKHR pickPresentMode(std::vector const& presentModes); +vk::SurfaceFormatKHR pickSurfaceFormat(std::vector const& formats); +void submitAndWait(vk::Device const& device, vk::Queue const& queue, vk::CommandBuffer const& commandBuffer); +void updateDescriptorSets( + vk::Device const& device, vk::DescriptorSet const& descriptorSet, + std::vector> const& bufferData, + uint32_t bindingOffset = 0); + +} // namespace vk::su + +std::ostream& operator<<(std::ostream& os, vk::su::UUID const& uuid);