#include "vulkan_adapter.h" #include "vulkan_common.h" #include #include #include #include #include #define GLM_FORCE_RADIANS #define GLM_FORCE_DEPTH_ZERO_TO_ONE #include #include #include #include #include #include "../../main/logging.h" #include "../../main/rendering.h" #include "vulkan_buffer.h" #include "vulkan_frame.h" #include "vulkan_pipeline.h" #include "vulkan_swap_chain.h" #include "vulkan_texture_descriptors.h" #include namespace progressia { namespace desktop { using progressia::main::Vertex; namespace { struct FieldProperties { uint32_t offset; VkFormat format; }; auto getVertexFieldProperties() { return std::array{ FieldProperties{offsetof(Vertex, position), VK_FORMAT_R32G32B32_SFLOAT}, FieldProperties{offsetof(Vertex, color), VK_FORMAT_R32G32B32A32_SFLOAT}, FieldProperties{offsetof(Vertex, normal), VK_FORMAT_R32G32B32_SFLOAT}, FieldProperties{offsetof(Vertex, texCoord), VK_FORMAT_R32G32_SFLOAT}, }; } } // namespace namespace { std::vector tmp_readFile(const std::string &path) { auto resource = __embedded_resources::getEmbeddedResource(path.c_str()); if (resource.data == nullptr) { // REPORT_ERROR progressia::main::logging::fatal() << "Could not find resource \"" << path << "\""; exit(1); } return std::vector(resource.data, resource.data + resource.length); } } // namespace Adapter::Adapter(Vulkan &vulkan) : vulkan(vulkan), viewUniform(0, vulkan), lightUniform(2, vulkan) { attachments.push_back( {"Depth buffer", vulkan.findSupportedFormat( {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT}, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT), VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_DONT_CARE, {1.0f, 0}, nullptr}); } Adapter::~Adapter() { // Do nothing } std::vector &Adapter::getAttachments() { return attachments; } std::vector Adapter::loadVertexShader() { return tmp_readFile("shader.vert.spv"); } std::vector Adapter::loadFragmentShader() { return tmp_readFile("shader.frag.spv"); } VkVertexInputBindingDescription Adapter::getVertexInputBindingDescription() { VkVertexInputBindingDescription bindingDescription{}; bindingDescription.binding = 0; bindingDescription.stride = sizeof(Vertex); bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; return bindingDescription; } std::vector Adapter::getVertexInputAttributeDescriptions() { std::vector attributeDescriptions; uint32_t i = 0; for (auto props : getVertexFieldProperties()) { attributeDescriptions.push_back({}); attributeDescriptions[i].binding = 0; attributeDescriptions[i].location = i; attributeDescriptions[i].format = props.format; attributeDescriptions[i].offset = props.offset; i++; } return attributeDescriptions; } std::vector Adapter::getUsedDSLayouts() const { return {viewUniform.getLayout(), vulkan.getTextureDescriptors().getLayout(), lightUniform.getLayout()}; } Adapter::ViewUniform::State Adapter::createView() { return viewUniform.addState(); } Adapter::LightUniform::State Adapter::createLight() { return lightUniform.addState(); } void Adapter::onPreFrame() { viewUniform.doUpdates(); lightUniform.doUpdates(); } /* * graphics_interface implementation */ } // namespace desktop namespace main { using namespace progressia::desktop; namespace { struct DrawRequest { progressia::desktop::Texture *texture; IndexedBuffer *vertices; glm::mat4 modelTransform; }; std::vector pendingDrawCommands; glm::mat4 currentModelTransform; } // namespace progressia::main::Texture::Texture(Backend backend) : backend(backend) {} progressia::main::Texture::~Texture() { delete static_cast(this->backend); } namespace { struct PrimitiveBackend { IndexedBuffer buf; progressia::main::Texture *tex; }; } // namespace Primitive::Primitive(Backend backend) : backend(backend) {} Primitive::~Primitive() { delete static_cast(this->backend); } void Primitive::draw() { auto backend = static_cast(this->backend); if (pendingDrawCommands.size() > 100000) { backend->buf.getVulkan().getGint().flush(); } pendingDrawCommands.push_back( {static_cast(backend->tex->backend), &backend->buf, currentModelTransform}); } const progressia::main::Texture *Primitive::getTexture() const { return static_cast(this->backend)->tex; } View::View(Backend backend) : backend(backend) {} View::~View() { delete static_cast(this->backend); } void View::configure(const glm::mat4 &proj, const glm::mat4 &view) { static_cast(this->backend) ->update(proj, view); } void View::use() { auto backend = static_cast(this->backend); backend->uniform->getVulkan().getGint().flush(); backend->bind(); } Light::Light(Backend backend) : backend(backend) {} Light::~Light() { delete static_cast(this->backend); } void Light::configure(const glm::vec3 &color, const glm::vec3 &from, float contrast, float softness) { static_cast(this->backend) ->update(Adapter::Light{glm::vec4(color, 1.0f), glm::vec4(glm::normalize(from), 1.0f), contrast, softness}); } void Light::use() { auto backend = static_cast(this->backend); backend->uniform->getVulkan().getGint().flush(); backend->bind(); } GraphicsInterface::GraphicsInterface(Backend backend) : backend(backend) {} GraphicsInterface::~GraphicsInterface() { // Do nothing } progressia::main::Texture * GraphicsInterface::newTexture(const progressia::main::Image &src) { auto backend = new progressia::desktop::Texture( src, *static_cast(this->backend)); return new Texture(backend); } Primitive * GraphicsInterface::newPrimitive(const std::vector &vertices, const std::vector &indices, progressia::main::Texture *texture) { auto backend = new PrimitiveBackend{ IndexedBuffer(vertices.size(), indices.size(), *static_cast(this->backend)), texture}; backend->buf.load(vertices.data(), indices.data()); return new Primitive(backend); } View *GraphicsInterface::newView() { return new View(new Adapter::ViewUniform::State( static_cast(this->backend)->getAdapter().createView())); } Light *GraphicsInterface::newLight() { return new Light(new Adapter::LightUniform::State( static_cast(this->backend)->getAdapter().createLight())); } glm::vec2 GraphicsInterface::getViewport() const { auto extent = static_cast(this->backend)->getSwapChain().getExtent(); return {extent.width, extent.height}; } void GraphicsInterface::setModelTransform(const glm::mat4 &m) { currentModelTransform = m; } void GraphicsInterface::flush() { auto commandBuffer = static_cast(this->backend) ->getCurrentFrame() ->getCommandBuffer(); auto pipelineLayout = static_cast(this->backend)->getPipeline().getLayout(); progressia::desktop::Texture *lastTexture = nullptr; for (auto &cmd : pendingDrawCommands) { if (cmd.texture != lastTexture) { lastTexture = cmd.texture; cmd.texture->bind(); } auto &m = cmd.modelTransform; // Evil transposition: column_major -> row_major // clang-format off std::remove_reference_t::value_type src[3*4] { m[0][0], m[0][1], m[0][2], m[0][3], m[1][0], m[1][1], m[1][2], m[1][3], m[2][0], m[2][1], m[2][2], m[2][3] }; // clang-format on vkCmdPushConstants( // REPORT_ERROR if getCurrentFrame() == nullptr commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(src), &src); cmd.vertices->draw(commandBuffer); } pendingDrawCommands.clear(); } float GraphicsInterface::tmp_getTime() { return glfwGetTime(); } uint64_t GraphicsInterface::getLastStartedFrame() { return static_cast(this->backend)->getLastStartedFrame(); } } // namespace main } // namespace progressia