4678 lines
146 KiB
C++
4678 lines
146 KiB
C++
#include <boost/asio/io_context.hpp>
|
||
#include <filesystem>
|
||
#include <memory>
|
||
#include <mutex>
|
||
#include <thread>
|
||
#include "Vulkan.hpp"
|
||
#include "Client/ServerSession.hpp"
|
||
#include "Common/Async.hpp"
|
||
#include "Common/Net.hpp"
|
||
#include "assets.hpp"
|
||
#include "imgui.h"
|
||
#include <GLFW/glfw3.h>
|
||
#ifdef HAS_IMGUI
|
||
#include <backends/imgui_impl_vulkan.h>
|
||
#include <backends/imgui_impl_glfw.h>
|
||
#endif
|
||
#include <freetype/ftglyph.h>
|
||
#include <vulkan/vulkan_core.h>
|
||
#include <algorithm>
|
||
#include <climits>
|
||
#include <cmath>
|
||
#include <cstddef>
|
||
#include <cstdlib>
|
||
#include <cstring>
|
||
#include <exception>
|
||
#include <functional>
|
||
#include <png++/png.hpp>
|
||
#include "VulkanRenderSession.hpp"
|
||
#include <Server/GameServer.hpp>
|
||
|
||
extern void LoadSymbolsVulkan(TOS::DynamicLibrary &library);
|
||
|
||
namespace LV::Client::VK {
|
||
|
||
struct ServerObj {
|
||
Server::GameServer GS;
|
||
Net::SocketServer LS;
|
||
|
||
ServerObj(asio::io_context &ioc)
|
||
: GS(ioc, ""), LS(ioc, [&](tcp::socket sock) -> coro<> { co_await GS.pushSocketConnect(std::move(sock)); }, 7890)
|
||
{
|
||
}
|
||
};
|
||
|
||
ByteBuffer loadPNG(std::ifstream &&read, int &width, int &height, bool &hasAlpha, bool flipOver)
|
||
{
|
||
png::image<png::rgba_pixel> img(read);
|
||
width = img.get_width();
|
||
height = img.get_height();
|
||
hasAlpha = true;
|
||
ByteBuffer buff(4*width*height);
|
||
for(int i = 0; i<height; i++)
|
||
std::copy(
|
||
((const char*) &img.get_pixbuf().operator [](i)[0]),
|
||
((const char*) &img.get_pixbuf().operator [](i)[0])+4*width,
|
||
(buff.data())+4*width*(flipOver ? height-i-1 : i));
|
||
return buff;
|
||
}
|
||
|
||
ByteBuffer loadPNG(std::istream &&read, int &width, int &height, bool &hasAlpha, bool flipOver)
|
||
{
|
||
png::image<png::rgba_pixel> img(read);
|
||
width = img.get_width();
|
||
height = img.get_height();
|
||
hasAlpha = true;
|
||
ByteBuffer buff(4*width*height);
|
||
for(int i = 0; i<height; i++)
|
||
std::copy(
|
||
((const char*) &img.get_pixbuf().operator [](i)[0]),
|
||
((const char*) &img.get_pixbuf().operator [](i)[0])+4*width,
|
||
(buff.data())+4*width*(flipOver ? height-i-1 : i));
|
||
return buff;
|
||
}
|
||
|
||
Vulkan::Vulkan(asio::io_context &ioc)
|
||
: IOC(ioc), GuardLock(ioc.get_executor())
|
||
{
|
||
Screen.Width = 1920/2;
|
||
Screen.Height = 1080/2;
|
||
getSettingsNext() = getBestSettings();
|
||
#ifdef NDEBUG
|
||
SettingsNext.Debug = false;
|
||
#endif
|
||
reInit();
|
||
|
||
Game.ImGuiInterfaces.push_back(&Vulkan::gui_MainMenu);
|
||
|
||
Game.MainThread = std::thread([&]() {
|
||
auto useLock = Game.UseLock.lock();
|
||
try {
|
||
run();
|
||
} catch(const std::exception &exc) {
|
||
LOG.error() << "Vulkan::run: " << exc.what();
|
||
}
|
||
|
||
try { Game.RSession = nullptr; } catch(const std::exception &exc) {
|
||
LOG.error() << "Game.RSession = nullptr: " << exc.what();
|
||
}
|
||
|
||
try { Game.Session = nullptr; } catch(const std::exception &exc) {
|
||
LOG.error() << "Game.Session = nullptr: " << exc.what();
|
||
}
|
||
|
||
try { Game.Server = nullptr; } catch(const std::exception &exc) {
|
||
LOG.error() << "Game.Server = nullptr: " << exc.what();
|
||
}
|
||
|
||
GuardLock.reset();
|
||
});
|
||
}
|
||
|
||
Vulkan::~Vulkan()
|
||
{
|
||
Game.UseLock.wait_no_use();
|
||
Game.MainThread.join();
|
||
|
||
for(std::shared_ptr<IVulkanDependent> dependent : ROS_Dependents)
|
||
dependent->free(this);
|
||
ROS_Dependents.clear();
|
||
|
||
deInitVulkan();
|
||
|
||
if(Graphics.Window)
|
||
{
|
||
glfwDestroyWindow(Graphics.Window);
|
||
Graphics.Window = nullptr;
|
||
}
|
||
}
|
||
|
||
void Vulkan::run()
|
||
{
|
||
NeedShutdown = false;
|
||
Graphics.ThisThread = std::this_thread::get_id();
|
||
|
||
VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0 };
|
||
VkSemaphore SemaphoreImageAcquired, SemaphoreDrawComplete;
|
||
|
||
vkAssert(!vkCreateSemaphore(Graphics.Device, &semaphoreCreateInfo, NULL, &SemaphoreImageAcquired));
|
||
vkAssert(!vkCreateSemaphore(Graphics.Device, &semaphoreCreateInfo, NULL, &SemaphoreDrawComplete));
|
||
|
||
|
||
double prevTime = glfwGetTime();
|
||
while(!NeedShutdown)
|
||
{
|
||
float dTime = glfwGetTime()-prevTime;
|
||
prevTime += dTime;
|
||
|
||
Screen.State = DrawState::Begin;
|
||
{
|
||
std::lock_guard lock(Screen.BeforeDrawMtx);
|
||
while(!Screen.BeforeDraw.empty())
|
||
{
|
||
Screen.BeforeDraw.front()(this);
|
||
Screen.BeforeDraw.pop();
|
||
}
|
||
}
|
||
|
||
if(!NeedShutdown && glfwWindowShouldClose(Graphics.Window)) {
|
||
NeedShutdown = true;
|
||
|
||
try {
|
||
if(Game.Session)
|
||
Game.Session->shutdown(EnumDisconnect::ByInterface);
|
||
} catch(const std::exception &exc) {
|
||
LOG.error() << "Game.Session->shutdown: " << exc.what();
|
||
}
|
||
|
||
try {
|
||
if(Game.Server)
|
||
Game.Server->GS.shutdown("Завершение работы из-за остановки клиента");
|
||
} catch(const std::exception &exc) {
|
||
LOG.error() << "Game.Server->GS.shutdown: " << exc.what();
|
||
}
|
||
}
|
||
|
||
if(Game.Session) {
|
||
ServerSession &sobj = *Game.Session;
|
||
|
||
// Спрятать или показать курсор
|
||
{
|
||
int mode = glfwGetInputMode(Graphics.Window, GLFW_CURSOR);
|
||
if(mode == GLFW_CURSOR_DISABLED && sobj.CursorMode != ISurfaceEventListener::EnumCursorMoveMode::MoveAndHidden)
|
||
glfwSetInputMode(Graphics.Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||
else if(mode == GLFW_CURSOR_NORMAL && sobj.CursorMode != ISurfaceEventListener::EnumCursorMoveMode::Default) {
|
||
glfwSetCursorPos(Graphics.Window, Screen.Width/2., Screen.Height/2.);
|
||
Game.MLastPosX = Screen.Width/2.;
|
||
Game.MLastPosY = Screen.Height/2.;
|
||
glfwSetInputMode(Graphics.Window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||
}
|
||
}
|
||
}
|
||
|
||
// if(CallBeforeDraw)
|
||
// CallBeforeDraw(this);
|
||
|
||
if(Game.RSession) {
|
||
Game.RSession->beforeDraw();
|
||
}
|
||
|
||
glfwPollEvents();
|
||
|
||
VkResult err;
|
||
err = vkAcquireNextImageKHR(Graphics.Device, Graphics.Swapchain, 1000000000ULL/20, SemaphoreImageAcquired, (VkFence) 0, &Graphics.DrawBufferCurrent);
|
||
GlobalTime gTime = glfwGetTime();
|
||
|
||
if (err == VK_ERROR_OUT_OF_DATE_KHR)
|
||
{
|
||
freeSwapchains();
|
||
buildSwapchains();
|
||
continue;
|
||
} else if (err == VK_SUBOPTIMAL_KHR)
|
||
{
|
||
LOGGER.debug() << "VK_SUBOPTIMAL_KHR Pre";
|
||
} else if(err == VK_SUCCESS) {
|
||
|
||
Screen.State = DrawState::Drawing;
|
||
//Готовим инструкции рисовки
|
||
{
|
||
const VkCommandBufferBeginInfo cmd_buf_info =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.pInheritanceInfo = nullptr
|
||
};
|
||
|
||
vkAssert(!vkBeginCommandBuffer(Graphics.CommandBufferRender, &cmd_buf_info));
|
||
}
|
||
|
||
{
|
||
VkImageMemoryBarrier image_memory_barrier =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||
.pNext = nullptr,
|
||
.srcAccessMask = 0,
|
||
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||
.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||
.image = Graphics.DrawBuffers[Graphics.DrawBufferCurrent].Image, // Graphics.InlineTexture.Image,
|
||
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
|
||
};
|
||
|
||
vkCmdPipelineBarrier(Graphics.CommandBufferRender, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||
0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
|
||
}
|
||
|
||
{
|
||
const VkClearValue clear_values[2] =
|
||
{
|
||
[0] = { .color = { .float32 = { 0.1f, 0.1f, 0.1f, 1.f }}},
|
||
[1] = { .depthStencil = { 1, 0 } },
|
||
};
|
||
|
||
const VkRenderPassBeginInfo rp_begin =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||
.pNext = nullptr,
|
||
.renderPass = Graphics.RenderPass,
|
||
.framebuffer = Graphics.DrawBuffers[Graphics.DrawBufferCurrent].FrameBuffer, //Graphics.InlineTexture.Frame,
|
||
.renderArea = VkRect2D {
|
||
.offset = {0, 0},
|
||
.extent = Screen.FrameExtent
|
||
},
|
||
.clearValueCount = 2,
|
||
.pClearValues = clear_values
|
||
};
|
||
|
||
vkCmdBeginRenderPass(Graphics.CommandBufferRender, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
|
||
}
|
||
|
||
|
||
{
|
||
VkViewport viewport = { 0.f, 0.f, float(Screen.Width), float(Screen.Height), 0.f, 1.f };
|
||
vkCmdSetViewport(Graphics.CommandBufferRender, 0, 1, &viewport);
|
||
|
||
VkRect2D scissor = { { int32_t(0), int32_t(0) }, { Screen.Width, Screen.Height } };
|
||
vkCmdSetScissor(Graphics.CommandBufferRender, 0, 1, &scissor);
|
||
}
|
||
|
||
if(Game.RSession) {
|
||
auto &robj = *Game.RSession;
|
||
// Рендер мира
|
||
|
||
robj.drawWorld(gTime, dTime, Graphics.CommandBufferRender);
|
||
|
||
uint16_t minSize = std::min(Screen.Width, Screen.Height);
|
||
glm::ivec2 interfaceSize = {int(Screen.Width*720/minSize), int(Screen.Height*720/minSize)};
|
||
}
|
||
|
||
// vkCmdEndRenderPass(Graphics.CommandBufferRender);
|
||
|
||
// {
|
||
// VkImageMemoryBarrier src_barrier =
|
||
// {
|
||
// .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||
// .pNext = nullptr,
|
||
// .srcAccessMask = 0,
|
||
// .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
||
// .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||
// .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||
// .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||
// .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||
// .image = Graphics.InlineTexture.Image,
|
||
// .subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
|
||
// };
|
||
|
||
// VkImageMemoryBarrier dst_barrier =
|
||
// {
|
||
// .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||
// .pNext = nullptr,
|
||
// .srcAccessMask = 0,
|
||
// .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||
// .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||
// .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||
// .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||
// .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||
// .image = Graphics.DrawBuffers[Graphics.DrawBufferCurrent].Image,
|
||
// .subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
|
||
// };
|
||
|
||
// vkCmdPipelineBarrier(
|
||
// Graphics.CommandBufferRender,
|
||
// VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
// VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
// 0,
|
||
// 0, nullptr,
|
||
// 0, nullptr,
|
||
// 1, &src_barrier
|
||
// );
|
||
|
||
// vkCmdPipelineBarrier(
|
||
// Graphics.CommandBufferRender,
|
||
// VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
// VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
// 0,
|
||
// 0, nullptr,
|
||
// 0, nullptr,
|
||
// 1, &dst_barrier
|
||
// );
|
||
|
||
// VkImageCopy copy_region =
|
||
// {
|
||
// .srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
|
||
// .srcOffset = {0, 0, 0},
|
||
// .dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
|
||
// .dstOffset = {0, 0, 0},
|
||
// .extent = {Screen.FrameExtent.width, Screen.FrameExtent.height, 1}
|
||
// };
|
||
|
||
// vkCmdCopyImage(
|
||
// Graphics.CommandBufferRender,
|
||
// Graphics.InlineTexture.Image,
|
||
// VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||
// Graphics.DrawBuffers[Graphics.DrawBufferCurrent].Image,
|
||
// VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||
// 1, ©_region
|
||
// );
|
||
|
||
// VkImageMemoryBarrier post_copy_barrier =
|
||
// {
|
||
// .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||
// .pNext = nullptr,
|
||
// .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||
// .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||
// .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||
// .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||
// .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||
// .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||
// .image = Graphics.DrawBuffers[Graphics.DrawBufferCurrent].Image,
|
||
// .subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
|
||
// };
|
||
|
||
// vkCmdPipelineBarrier(
|
||
// Graphics.CommandBufferRender,
|
||
// VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
// VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||
// 0,
|
||
// 0, nullptr,
|
||
// 0, nullptr,
|
||
// 1, &post_copy_barrier
|
||
// );
|
||
// }
|
||
|
||
// {
|
||
// VkImageMemoryBarrier prePresentBarrier =
|
||
// {
|
||
// .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||
// .pNext = nullptr,
|
||
// .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||
// .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
|
||
// .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||
// .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||
// .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||
// .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||
// .image = Graphics.InlineTexture.Image,
|
||
// .subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
|
||
// };
|
||
|
||
// vkCmdPipelineBarrier(Graphics.CommandBufferRender, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
||
// 0, 0, nullptr, 0, nullptr, 1, &prePresentBarrier);
|
||
// }
|
||
|
||
// {
|
||
// VkImageMemoryBarrier image_memory_barrier =
|
||
// {
|
||
// .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||
// .pNext = nullptr,
|
||
// .srcAccessMask = 0,
|
||
// .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||
// .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||
// .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||
// .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||
// .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||
// .image = Graphics.DrawBuffers[Graphics.DrawBufferCurrent].Image,
|
||
// .subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
|
||
// };
|
||
|
||
// vkCmdPipelineBarrier(Graphics.CommandBufferRender, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||
// 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
|
||
// }
|
||
|
||
// {
|
||
// const VkClearValue clear_values[2] =
|
||
// {
|
||
// [0] = { .color = { .float32 = { 0.1f, 0.1f, 0.1f, 1.0f }}},
|
||
// [1] = { .depthStencil = { 1, 0 } },
|
||
// };
|
||
|
||
// const VkRenderPassBeginInfo rp_begin =
|
||
// {
|
||
// .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||
// .pNext = nullptr,
|
||
// .renderPass = Graphics.RenderPass,
|
||
// .framebuffer = Graphics.DrawBuffers[Graphics.DrawBufferCurrent].FrameBuffer,
|
||
// .renderArea = VkRect2D {
|
||
// .offset = {0, 0},
|
||
// .extent = Screen.FrameExtent
|
||
// },
|
||
// .clearValueCount = 2,
|
||
// .pClearValues = clear_values
|
||
// };
|
||
|
||
// vkCmdBeginRenderPass(Graphics.CommandBufferRender, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
|
||
// }
|
||
|
||
|
||
|
||
#ifdef HAS_IMGUI
|
||
ImGui_ImplVulkan_NewFrame();
|
||
ImGui_ImplGlfw_NewFrame();
|
||
ImGui::NewFrame();
|
||
|
||
ImGui::SetNextWindowPos({0, 0});
|
||
ImGui::SetNextWindowSize({(float) Screen.Width, (float) Screen.Height});
|
||
|
||
vkAssert(Game.ImGuiInterfaces.size());
|
||
(this->*Game.ImGuiInterfaces.back())();
|
||
|
||
ImGui::Render();
|
||
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), Graphics.CommandBufferRender);
|
||
#endif
|
||
|
||
vkCmdEndRenderPass(Graphics.CommandBufferRender);
|
||
|
||
{
|
||
VkImageMemoryBarrier prePresentBarrier =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||
.pNext = nullptr,
|
||
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
|
||
.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||
.image = Graphics.DrawBuffers[Graphics.DrawBufferCurrent].Image,
|
||
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
|
||
};
|
||
|
||
vkCmdPipelineBarrier(Graphics.CommandBufferRender, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
||
0, 0, nullptr, 0, nullptr, 1, &prePresentBarrier);
|
||
}
|
||
|
||
vkAssert(!vkEndCommandBuffer(Graphics.CommandBufferRender));
|
||
|
||
{
|
||
VkFence nullFence = VK_NULL_HANDLE;
|
||
VkPipelineStageFlags pipe_stage_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||
VkSubmitInfo submit_info =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||
.pNext = nullptr,
|
||
.waitSemaphoreCount = 1,
|
||
.pWaitSemaphores = &SemaphoreImageAcquired,
|
||
.pWaitDstStageMask = &pipe_stage_flags,
|
||
.commandBufferCount = 1,
|
||
.pCommandBuffers = &Graphics.CommandBufferRender,
|
||
.signalSemaphoreCount = 1,
|
||
.pSignalSemaphores = &SemaphoreDrawComplete
|
||
};
|
||
|
||
//Рисуем, когда получим картинку
|
||
vkAssert(!vkQueueSubmit(Graphics.DeviceQueueGraphic, 1, &submit_info, nullFence));
|
||
}
|
||
|
||
{
|
||
VkPresentInfoKHR present =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
||
.pNext = NULL,
|
||
.waitSemaphoreCount = 1,
|
||
.pWaitSemaphores = &SemaphoreDrawComplete,
|
||
.swapchainCount = 1,
|
||
.pSwapchains = &Graphics.Swapchain,
|
||
.pImageIndices = &Graphics.DrawBufferCurrent
|
||
};
|
||
|
||
// Завершаем картинку
|
||
err = vkQueuePresentKHR(Graphics.DeviceQueueGraphic, &present);
|
||
if (err == VK_ERROR_OUT_OF_DATE_KHR)
|
||
{
|
||
freeSwapchains();
|
||
buildSwapchains();
|
||
} else if (err == VK_SUBOPTIMAL_KHR)
|
||
LOGGER.debug() << "VK_SUBOPTIMAL_KHR Post";
|
||
else
|
||
vkAssert(!err);
|
||
}
|
||
} else
|
||
vkAssert(err == VK_TIMEOUT);
|
||
|
||
if(Game.Session) {
|
||
Game.Session->atFreeDrawTime(gTime, dTime);
|
||
}
|
||
|
||
vkAssert(!vkQueueWaitIdle(Graphics.DeviceQueueGraphic));
|
||
|
||
vkDeviceWaitIdle(Graphics.Device);
|
||
Screen.State = DrawState::End;
|
||
}
|
||
|
||
vkDestroySemaphore(Graphics.Device, SemaphoreImageAcquired, nullptr);
|
||
vkDestroySemaphore(Graphics.Device, SemaphoreDrawComplete, nullptr);
|
||
}
|
||
|
||
void Vulkan::glfwCallbackError(int error, const char *description)
|
||
{
|
||
Logger("Vulkan").error() << "glfwError " << error << ": " << description;
|
||
}
|
||
|
||
uint32_t Vulkan::memoryTypeFromProperties(uint32_t bitsOfAcceptableTypes, VkFlags bitsOfAccessMask)
|
||
{
|
||
// Просматриваем допустимые типы памяти bitsOfAcceptableTypes
|
||
for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++, bitsOfAcceptableTypes >>= 1)
|
||
if ((bitsOfAcceptableTypes & 1) == 1)
|
||
// Сверяем с маской необходимого доступа к памяти
|
||
if ((Graphics.DeviceMemoryProperties.memoryTypes[i].propertyFlags & bitsOfAccessMask) == bitsOfAccessMask)
|
||
// Нашли
|
||
return i;
|
||
|
||
|
||
MAKE_ERROR("Требуемый тип памяти отсутствует, bitsOfAcceptableTypes: "
|
||
<< bitsOfAcceptableTypes << ", bitsOfAccessMask: "
|
||
<< uint32_t(bitsOfAccessMask));
|
||
}
|
||
|
||
void Vulkan::freeSwapchains()
|
||
{
|
||
//vkDeviceWaitIdle(Screen.Device);
|
||
|
||
if(Graphics.Instance && Graphics.Device)
|
||
{
|
||
std::vector<VkImageView> oldViews;
|
||
std::vector<VkFramebuffer> oldFrames;
|
||
oldViews.push_back(Graphics.DepthView);
|
||
|
||
if(Graphics.InlineTexture.View)
|
||
{
|
||
oldViews.push_back(Graphics.InlineTexture.View);
|
||
Graphics.InlineTexture.View = nullptr;
|
||
}
|
||
|
||
if(Graphics.InlineTexture.Frame)
|
||
{
|
||
oldFrames.push_back(Graphics.InlineTexture.Frame);
|
||
Graphics.InlineTexture.Frame = nullptr;
|
||
}
|
||
|
||
for (uint32_t i = 0; i < Graphics.DrawBuffers.size(); i++)
|
||
{
|
||
auto &obj = Graphics.DrawBuffers[i];
|
||
|
||
if(obj.FrameBuffer)
|
||
oldFrames.push_back(obj.FrameBuffer);
|
||
|
||
if(obj.View)
|
||
oldViews.push_back(obj.View);
|
||
|
||
if(obj.Cmd)
|
||
vkFreeCommandBuffers(Graphics.Device, Graphics.Pool, 1, &obj.Cmd);
|
||
|
||
|
||
// Изображения предоставлены SwapChain'ом, удаляются автоматически
|
||
/*if(obj.Image)
|
||
{
|
||
vkDestroyImage(Graphics.Device, obj.Image, nullptr);
|
||
obj.Image = nullptr;
|
||
}*/
|
||
}
|
||
|
||
|
||
beforeDraw([oldViews = std::move(oldViews), oldFrames = std::move(oldFrames),
|
||
depthImage = Graphics.DepthImage, depthMemory = Graphics.DepthMemory, inlineImage = Graphics.InlineTexture.Image, inlineMemory = Graphics.InlineTexture.Memory](Vulkan *instance)
|
||
{
|
||
for(auto &iter : oldFrames)
|
||
vkDestroyFramebuffer(instance->Graphics.Device, iter, nullptr);
|
||
|
||
for(auto &iter : oldViews)
|
||
vkDestroyImageView(instance->Graphics.Device, iter, nullptr);
|
||
|
||
vkDestroyImage(instance->Graphics.Device, depthImage, nullptr);
|
||
vkFreeMemory(instance->Graphics.Device, depthMemory, nullptr);
|
||
|
||
vkDestroyImage(instance->Graphics.Device, inlineImage, nullptr);
|
||
vkFreeMemory(instance->Graphics.Device, inlineMemory, nullptr);
|
||
});
|
||
|
||
Graphics.InlineTexture.Image = nullptr;
|
||
Graphics.InlineTexture.Memory = nullptr;
|
||
}
|
||
|
||
Graphics.DepthView = nullptr;
|
||
Graphics.DepthImage = nullptr;
|
||
Graphics.DepthMemory = nullptr;
|
||
Graphics.DrawBuffers.clear();
|
||
}
|
||
|
||
|
||
static void check_vk_result(VkResult err)
|
||
{
|
||
if (err == 0)
|
||
return;
|
||
fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err);
|
||
if (err < 0)
|
||
abort();
|
||
}
|
||
|
||
void Vulkan::buildSwapchains()
|
||
{
|
||
vkAssert(Graphics.PhysicalDevice);
|
||
vkAssert(Graphics.Surface);
|
||
vkAssert(Graphics.Window);
|
||
|
||
// Определение нового размера буфера
|
||
std::stringstream report;
|
||
report << "Пересоздание цепочки вывода, текущий размер окна: " << Screen.Width << " x " << Screen.Height << '\n';
|
||
|
||
VkSurfaceCapabilitiesKHR surfaceCapabilities;
|
||
vkAssert(!vkGetPhysicalDeviceSurfaceCapabilitiesKHR(Graphics.PhysicalDevice, Graphics.Surface, &surfaceCapabilities));
|
||
|
||
uint32_t count = -1;
|
||
VkExtent2D swapchainExtent;
|
||
if (surfaceCapabilities.currentExtent.width == 0xFFFFFFFF) {
|
||
// Если размер поверхности не определен, размер устанавливается
|
||
// равным размеру запрашиваемых изображений, который должен
|
||
// соответствовать минимальным и максимальным значениям.
|
||
|
||
report << "Оконная подсистема не сообщила о размере буфера (surfaceCapabilities.currentExtent.width == 0xFFFFFFFF)\n";
|
||
int width, height;
|
||
glfwGetFramebufferSize(Graphics.Window, &width, &height);
|
||
report << "Размер определён под запросу glfw: " << width << " x " << height << '\n';
|
||
report << "Приводим к ограничениям предоставленным оконной подсистемой:\n\tmin("
|
||
<< surfaceCapabilities.minImageExtent.width << " x "
|
||
<< surfaceCapabilities.minImageExtent.height
|
||
<< ") / max(" << surfaceCapabilities.maxImageExtent.width
|
||
<< " x " << surfaceCapabilities.maxImageExtent.height
|
||
<< ")\n";
|
||
|
||
swapchainExtent.width = std::clamp((uint32_t) width, surfaceCapabilities.minImageExtent.width, surfaceCapabilities.maxImageExtent.width);
|
||
swapchainExtent.height = std::clamp((uint32_t) height, surfaceCapabilities.minImageExtent.height, surfaceCapabilities.maxImageExtent.height);
|
||
report << "Утверждённый размер экранного буфера: "
|
||
<< swapchainExtent.width << " x " << swapchainExtent.height << '\n';
|
||
} else {
|
||
swapchainExtent = surfaceCapabilities.currentExtent;
|
||
report << "Размер буффера, предоставленный оконной подсистемой: "
|
||
<< swapchainExtent.width << " x "
|
||
<< swapchainExtent.height << '\n';
|
||
}
|
||
|
||
Screen.FrameExtent = swapchainExtent;
|
||
|
||
// Определение количества сменных буферов
|
||
VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
|
||
uint32_t desiredNumOfSwapchainImages = surfaceCapabilities.minImageCount;
|
||
if (surfaceCapabilities.maxImageCount > 0 && desiredNumOfSwapchainImages > surfaceCapabilities.maxImageCount)
|
||
desiredNumOfSwapchainImages = surfaceCapabilities.maxImageCount;
|
||
|
||
report << "Количество изображений в цепочке смены кадров: min(" << surfaceCapabilities.minImageCount
|
||
<< ") / max(" << surfaceCapabilities.maxImageCount
|
||
<< "); Запрошено оконной подсистемой: "
|
||
<< surfaceCapabilities.minImageCount << "; утверждено: "
|
||
<< desiredNumOfSwapchainImages << '\n';
|
||
|
||
VkSurfaceTransformFlagsKHR preTransform;
|
||
if (surfaceCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
|
||
{
|
||
preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||
report << "Используемая трансформация: VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR\n";
|
||
} else {
|
||
preTransform = surfaceCapabilities.currentTransform;
|
||
report << "Используемая трансформация: Не VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR\n";
|
||
}
|
||
|
||
// Пересоздание Swapchain
|
||
VkSwapchainKHR oldSwapchain = Graphics.Swapchain;
|
||
const VkSwapchainCreateInfoKHR swapchainInfo =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.surface = Graphics.Surface,
|
||
.minImageCount = desiredNumOfSwapchainImages,
|
||
.imageFormat = Graphics.SurfaceFormat,
|
||
.imageColorSpace = Graphics.SurfaceColorSpace,
|
||
.imageExtent = swapchainExtent,
|
||
.imageArrayLayers = 1,
|
||
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
|
||
| VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
||
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||
.queueFamilyIndexCount = 0,
|
||
.pQueueFamilyIndices = nullptr,
|
||
.preTransform = VkSurfaceTransformFlagBitsKHR(preTransform),
|
||
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
||
.presentMode = swapchainPresentMode,
|
||
.clipped = true,
|
||
.oldSwapchain = oldSwapchain
|
||
};
|
||
|
||
vkAssert(!vkCreateSwapchainKHR(Graphics.Device, &swapchainInfo, nullptr, &Graphics.Swapchain));
|
||
|
||
if (oldSwapchain != VK_NULL_HANDLE)
|
||
vkDestroySwapchainKHR(Graphics.Device, oldSwapchain, nullptr);
|
||
|
||
// Получение сменных буферов
|
||
vkAssert(!vkGetSwapchainImagesKHR(Graphics.Device, Graphics.Swapchain, &count, nullptr));
|
||
std::vector<VkImage> swapchainImages(count);
|
||
vkAssert(!vkGetSwapchainImagesKHR(Graphics.Device, Graphics.Swapchain, &count, swapchainImages.data()));
|
||
|
||
Graphics.DrawBuffers.resize(count);
|
||
Graphics.DrawBufferCount = count;
|
||
report << "Получено сменых изображений цепочки: " << count << '\n';
|
||
|
||
// Создание отображений для полученых изображений буферов
|
||
for(uint32_t iter = 0; iter < count; iter++)
|
||
{
|
||
VkImageViewCreateInfo color_attachment_view =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.image = swapchainImages[iter],
|
||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||
.format = Graphics.SurfaceFormat,
|
||
.components = VkComponentMapping
|
||
{
|
||
VK_COMPONENT_SWIZZLE_R,
|
||
VK_COMPONENT_SWIZZLE_G,
|
||
VK_COMPONENT_SWIZZLE_B,
|
||
VK_COMPONENT_SWIZZLE_A
|
||
},
|
||
|
||
.subresourceRange = VkImageSubresourceRange
|
||
{
|
||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||
.baseMipLevel = 0,
|
||
.levelCount = 1,
|
||
.baseArrayLayer = 0,
|
||
.layerCount = 1
|
||
}
|
||
};
|
||
|
||
Graphics.DrawBuffers[iter].Image = swapchainImages[iter];
|
||
vkAssert(!vkCreateImageView(Graphics.Device, &color_attachment_view, nullptr, &Graphics.DrawBuffers[iter].View));
|
||
}
|
||
|
||
// Текущий рабочий буфер обнуляется
|
||
Graphics.DrawBufferCurrent = 0;
|
||
|
||
// Пересоздание буфера глубины
|
||
const VkImageCreateInfo depthImage =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.imageType = VK_IMAGE_TYPE_2D,
|
||
.format = Graphics.DepthFormat,
|
||
.extent = { swapchainExtent.width, swapchainExtent.height, 1 },
|
||
.mipLevels = 1,
|
||
.arrayLayers = 1,
|
||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||
.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
|
||
};
|
||
|
||
vkAssert(!vkCreateImage(Graphics.Device, &depthImage, nullptr, &Graphics.DepthImage));
|
||
|
||
// Самостоятельно выделяем память под буфер
|
||
VkMemoryRequirements memReqs;
|
||
vkGetImageMemoryRequirements(Graphics.Device, Graphics.DepthImage, &memReqs);
|
||
|
||
VkMemoryAllocateInfo memAlloc =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||
.pNext = nullptr,
|
||
.allocationSize = memReqs.size,
|
||
.memoryTypeIndex = memoryTypeFromProperties(memReqs.memoryTypeBits, 0)
|
||
};
|
||
|
||
vkAssert(!vkAllocateMemory(Graphics.Device, &memAlloc, nullptr, &Graphics.DepthMemory));
|
||
vkAssert(!vkBindImageMemory(Graphics.Device, Graphics.DepthImage, Graphics.DepthMemory, 0));
|
||
|
||
// Синхронизация формата изображения
|
||
VkImageMemoryBarrier infoImageMemoryBarrier =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||
.pNext = nullptr,
|
||
.srcAccessMask = VK_ACCESS_NONE,
|
||
.dstAccessMask = VK_ACCESS_NONE,
|
||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||
.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
||
.srcQueueFamilyIndex = 0,
|
||
.dstQueueFamilyIndex = 0,
|
||
.image = Graphics.DepthImage,
|
||
.subresourceRange = { VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1 }
|
||
};
|
||
|
||
vkCmdPipelineBarrier(Graphics.CommandBufferData, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||
0, 0, nullptr, 0, nullptr, 1, &infoImageMemoryBarrier);
|
||
//setImageLayout(Depth.image, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_ACCESS_NONE);
|
||
|
||
// Создаём отображение для буфера глубины
|
||
VkImageViewCreateInfo view =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.image = Graphics.DepthImage,
|
||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||
.format = Graphics.DepthFormat,
|
||
.components = { VK_COMPONENT_SWIZZLE_IDENTITY },
|
||
.subresourceRange = VkImageSubresourceRange
|
||
{
|
||
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
|
||
.baseMipLevel = 0,
|
||
.levelCount = 1,
|
||
.baseArrayLayer = 0,
|
||
.layerCount = 1
|
||
}
|
||
};
|
||
|
||
vkAssert(!vkCreateImageView(Graphics.Device, &view, nullptr, &Graphics.DepthView));
|
||
|
||
if(!Graphics.InlineTexture.Image) {
|
||
VkImageCreateInfo imageCreateInfo = {
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.imageType = VK_IMAGE_TYPE_2D,
|
||
.format = Graphics.SurfaceFormat,
|
||
.extent = {swapchainExtent.width, swapchainExtent.height, 1},
|
||
.mipLevels = 1,
|
||
.arrayLayers = 1,
|
||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||
.usage =
|
||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
|
||
| VK_IMAGE_USAGE_TRANSFER_DST_BIT
|
||
| VK_IMAGE_USAGE_SAMPLED_BIT
|
||
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
|
||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||
.queueFamilyIndexCount = 0,
|
||
.pQueueFamilyIndices = nullptr,
|
||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED
|
||
};
|
||
|
||
vkAssert(!vkCreateImage(Graphics.Device, &imageCreateInfo , nullptr , &Graphics.InlineTexture.Image));
|
||
}
|
||
|
||
if(!Graphics.InlineTexture.Memory) {
|
||
VkMemoryRequirements memoryReqs;
|
||
const VkImageSubresource memorySubres = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, .arrayLayer = 0, };
|
||
|
||
vkGetImageMemoryRequirements(Graphics.Device, Graphics.InlineTexture.Image, &memoryReqs);
|
||
|
||
VkMemoryAllocateInfo memoryAlloc =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||
.pNext = nullptr,
|
||
.allocationSize = memoryReqs.size,
|
||
.memoryTypeIndex = memoryTypeFromProperties(memoryReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
|
||
};
|
||
|
||
vkAssert(!vkAllocateMemory(Graphics.Device, &memoryAlloc, nullptr, &Graphics.InlineTexture.Memory));
|
||
vkAssert(!vkBindImageMemory(Graphics.Device, Graphics.InlineTexture.Image, Graphics.InlineTexture.Memory, 0));
|
||
|
||
VkImageMemoryBarrier prePresentBarrier =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||
.pNext = nullptr,
|
||
.srcAccessMask = 0,
|
||
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
|
||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||
.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||
.image = Graphics.InlineTexture.Image,
|
||
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
|
||
};
|
||
|
||
vkCmdPipelineBarrier(Graphics.CommandBufferData, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||
0, 0, nullptr, 0, nullptr, 1, &prePresentBarrier);
|
||
|
||
}
|
||
|
||
if(!Graphics.InlineTexture.View) {
|
||
view.image = Graphics.InlineTexture.Image;
|
||
view.format = VK_FORMAT_B8G8R8A8_UNORM;
|
||
view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||
view.components = {
|
||
VK_COMPONENT_SWIZZLE_IDENTITY
|
||
};
|
||
vkAssert(!vkCreateImageView(Graphics.Device, &view, nullptr, &Graphics.InlineTexture.View));
|
||
}
|
||
|
||
if(!Graphics.InlineTexture.Frame) {
|
||
VkImageView attachments[2];
|
||
attachments[0] = Graphics.InlineTexture.View;
|
||
attachments[1] = Graphics.DepthView;
|
||
const VkFramebufferCreateInfo infoFb =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.renderPass = Graphics.RenderPass,
|
||
.attachmentCount = 2,
|
||
.pAttachments = attachments,
|
||
.width = swapchainExtent.width,
|
||
.height = swapchainExtent.height,
|
||
.layers = 1
|
||
};
|
||
|
||
vkAssert(!vkCreateFramebuffer(Graphics.Device, &infoFb, nullptr, &Graphics.InlineTexture.Frame));
|
||
}
|
||
|
||
// Создаём экранные буферы глубины, связанные с одним кадровым буфером глубины
|
||
VkImageView attachments[2];
|
||
attachments[1] = Graphics.DepthView;
|
||
|
||
const VkFramebufferCreateInfo infoFb =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.renderPass = Graphics.RenderPass,
|
||
.attachmentCount = 2,
|
||
.pAttachments = attachments,
|
||
.width = swapchainExtent.width,
|
||
.height = swapchainExtent.height,
|
||
.layers = 1
|
||
};
|
||
|
||
for (uint32_t iter = 0; iter < Graphics.DrawBufferCount; iter++)
|
||
{
|
||
attachments[0] = Graphics.DrawBuffers[iter].View;
|
||
vkAssert(!vkCreateFramebuffer(Graphics.Device, &infoFb, nullptr, &Graphics.DrawBuffers[iter].FrameBuffer));
|
||
}
|
||
|
||
// Передача информации о количестве сменных буферов в ImGui
|
||
#ifdef HAS_IMGUI
|
||
if(ImGui::GetCurrentContext())
|
||
ImGui_ImplVulkan_SetMinImageCount(Graphics.DrawBufferCount);
|
||
#endif
|
||
|
||
Graphics.SwapchainChangeReport = report.str();
|
||
flushCommandBufferData();
|
||
}
|
||
|
||
void Vulkan::glfwCallbackOnResize(GLFWwindow *window, int width, int height)
|
||
{
|
||
Vulkan *handler = (Vulkan*) glfwGetWindowUserPointer(window);
|
||
|
||
if(handler)
|
||
{
|
||
handler->Screen.Width = width;
|
||
handler->Screen.Height = height;
|
||
handler->freeSwapchains();
|
||
handler->buildSwapchains();
|
||
|
||
if(handler->Game.Session) {
|
||
handler->Game.Session->onResize(width, height);
|
||
|
||
if(handler->Game.Session->CursorMode == ISurfaceEventListener::EnumCursorMoveMode::MoveAndHidden) {
|
||
glfwSetCursorPos(window, width/2., height/2.);
|
||
handler->Game.MLastPosX = width/2.;
|
||
handler->Game.MLastPosY = height/2.;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void Vulkan::glfwCallbackOnMouseButton(GLFWwindow* window, int button, int action, int mods)
|
||
{
|
||
Vulkan *handler = (Vulkan*) glfwGetWindowUserPointer(window);
|
||
|
||
if(handler->Game.Session)
|
||
handler->Game.Session->onCursorBtn((ISurfaceEventListener::EnumCursorBtn) button, action);
|
||
}
|
||
|
||
void Vulkan::glfwCallbackOnCursorPos(GLFWwindow* window, double xpos, double ypos)
|
||
{
|
||
Vulkan *handler = (Vulkan*) glfwGetWindowUserPointer(window);
|
||
|
||
if(handler->Game.Session) {
|
||
ServerSession &sobj = *handler->Game.Session;
|
||
if(sobj.CursorMode == ISurfaceEventListener::EnumCursorMoveMode::Default) {
|
||
sobj.onCursorPosChange((int32_t) xpos, (int32_t) ypos);
|
||
} else {
|
||
sobj.onCursorMove(xpos-handler->Game.MLastPosX, handler->Game.MLastPosY-ypos);
|
||
handler->Game.MLastPosX = xpos;
|
||
handler->Game.MLastPosY = ypos;
|
||
}
|
||
}
|
||
}
|
||
|
||
void Vulkan::glfwCallbackOnScale(GLFWwindow* window, float xscale, float yscale)
|
||
{
|
||
Vulkan *handler = (Vulkan*) glfwGetWindowUserPointer(window);
|
||
|
||
}
|
||
|
||
void Vulkan::glfwCallbackOnKey(GLFWwindow* window, int key, int scancode, int action, int mods)
|
||
{
|
||
Vulkan *handler = (Vulkan*) glfwGetWindowUserPointer(window);
|
||
if(handler->Game.Session)
|
||
handler->Game.Session->onKeyboardBtn(key, action);
|
||
}
|
||
|
||
void Vulkan::glfwCallbackOnFocus(GLFWwindow* window, int focused)
|
||
{
|
||
Vulkan *handler = (Vulkan*) glfwGetWindowUserPointer(window);
|
||
if(handler->Game.Session)
|
||
handler->Game.Session->onChangeFocusState(focused);
|
||
}
|
||
|
||
void Vulkan::checkLibrary()
|
||
{
|
||
if(LibraryVulkan)
|
||
return;
|
||
|
||
std::stringstream error;
|
||
|
||
try {
|
||
// Подгружаем библиотеку
|
||
#ifdef _WIN32
|
||
LibraryVulkan.emplace("vulkan-1");
|
||
#else
|
||
LibraryVulkan.emplace("libvulkan.so.1");
|
||
#endif
|
||
|
||
// Подгружаем символы
|
||
try { LoadSymbolsVulkan(*LibraryVulkan); }
|
||
catch(const std::exception &exc) {
|
||
error << "Ошибка загрузки символов библиотеки Vulkan:\n" << exc.what() << '\n';
|
||
goto onError;
|
||
}
|
||
|
||
// Инициализация GLFW
|
||
if(!glfwInit())
|
||
{
|
||
const char *error_msg;
|
||
int err_code = glfwGetError(&error_msg);
|
||
error << "Не удалось инициализировать GLFW (glfwInit). Код: " << err_code << ". Ошибка: " << error_msg << '\n';
|
||
goto onError;
|
||
}
|
||
|
||
// Дополнительный тест GLFW на присутствие
|
||
// минимального функционала Vulkan ICD
|
||
if (!glfwVulkanSupported())
|
||
{
|
||
const char *error_msg;
|
||
int err_code = glfwGetError(&error_msg);
|
||
error << "GLFW сообщает об отсутствии минимально необходимого функционала Vulkan ICD (glfwVulkanSupported), код: " << err_code << ", ошибка: " << error_msg << '\n';
|
||
// Просто уведомляем, не создаём ошибку
|
||
}
|
||
|
||
// Загрузим доступные слои
|
||
{
|
||
uint32_t count = -1;
|
||
|
||
vkAssert(!vkEnumerateInstanceLayerProperties(&count, nullptr));
|
||
vkAssert(count != -1);
|
||
|
||
if(count)
|
||
{
|
||
std::vector<VkLayerProperties> layerProperties(count);
|
||
vkAssert(!vkEnumerateInstanceLayerProperties(&count, layerProperties.data()));
|
||
|
||
Graphics.InstanceLayers.resize(count);
|
||
|
||
auto *ptrFrom = layerProperties.data();
|
||
auto *ptrTo = Graphics.InstanceLayers.data();
|
||
for(uint32_t iter = 0; iter < count; iter++, ptrFrom++, ptrTo++)
|
||
{
|
||
ptrTo->LayerName = ptrFrom->layerName;
|
||
ptrTo->SpecVersion = ptrFrom->specVersion;
|
||
ptrTo->ImplementationVersion = ptrFrom->implementationVersion;
|
||
ptrTo->Description = ptrFrom->description;
|
||
}
|
||
} else
|
||
Graphics.InstanceLayers.clear();
|
||
}
|
||
|
||
// Загрузим доступные расширения
|
||
{
|
||
uint32_t count = -1;
|
||
|
||
vkAssert(!vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr));
|
||
vkAssert(count != -1);
|
||
|
||
if(count)
|
||
{
|
||
std::vector<VkExtensionProperties> extensionProperties(count);
|
||
vkAssert(!vkEnumerateInstanceExtensionProperties(nullptr, &count, extensionProperties.data()));
|
||
|
||
Graphics.InstanceExtensions.resize(count);
|
||
|
||
auto *ptrFrom = extensionProperties.data();
|
||
auto *ptrTo = Graphics.InstanceExtensions.data();
|
||
for(uint32_t iter = 0; iter < count; iter++, ptrFrom++, ptrTo++)
|
||
{
|
||
ptrTo->ExtensionName = ptrFrom->extensionName;
|
||
ptrTo->SpecVersion = ptrFrom->specVersion;
|
||
}
|
||
} else
|
||
Graphics.InstanceExtensions.clear();
|
||
}
|
||
|
||
// Загрузим расширения, необходимые GLFW
|
||
{
|
||
uint32_t count = -1;
|
||
const char **extensionsRequired = glfwGetRequiredInstanceExtensions(&count);
|
||
|
||
if(!extensionsRequired)
|
||
{
|
||
error << "Функция glfwGetRequiredInstanceExtensions не предоставила необходимые расширения\n";
|
||
goto onError;
|
||
}
|
||
|
||
vkAssert(count != -1);
|
||
|
||
if(count)
|
||
{
|
||
Graphics.GLFWExtensions.resize(count);
|
||
auto *ptrFrom = extensionsRequired;
|
||
auto *ptrTo = Graphics.GLFWExtensions.data();
|
||
for(uint32_t iter = 0; iter < count; iter++, ptrFrom++, ptrTo++)
|
||
*ptrTo = *ptrFrom;
|
||
} else
|
||
Graphics.GLFWExtensions.clear();
|
||
}
|
||
|
||
// Создадим instance для запроса информации о доступных
|
||
// устройствах и их характеристиках
|
||
vkInstance localInstance(this);
|
||
std::vector<VkPhysicalDevice> devices;
|
||
{
|
||
uint32_t count = -1;
|
||
VkResult res;
|
||
|
||
vkAssert(!vkEnumeratePhysicalDevices(localInstance.getInstance(), &count, nullptr));
|
||
|
||
if(!count)
|
||
{
|
||
error << "vkEnumeratePhysicalDevices сообщил об отсутствии подходящего устройства\n";
|
||
goto onError;
|
||
}
|
||
|
||
devices.resize(count);
|
||
vkAssert(!vkEnumeratePhysicalDevices(localInstance.getInstance(), &count, devices.data()));
|
||
Graphics.Devices.resize(count);
|
||
|
||
// Перебор устройств
|
||
for(size_t iter = 0; iter < count; iter++)
|
||
{
|
||
// Информация об устройстве
|
||
VkPhysicalDeviceProperties deviceProperties;
|
||
auto &device = Graphics.Devices[iter];
|
||
vkGetPhysicalDeviceProperties(devices[iter], &deviceProperties);
|
||
vkGetPhysicalDeviceFeatures(devices[iter], &device.DeviceFeatures);
|
||
|
||
// Копируем характеристики устройства
|
||
device.ApiVersion = deviceProperties.apiVersion;
|
||
device.DriverVersion = deviceProperties.driverVersion;
|
||
device.VendorID = deviceProperties.vendorID;
|
||
device.DeviceID = deviceProperties.deviceID;
|
||
device.DeviceType = deviceProperties.deviceType;
|
||
device.DeviceName = deviceProperties.deviceName;
|
||
device.Limits = deviceProperties.limits;
|
||
device.SparseProperties = deviceProperties.sparseProperties;
|
||
|
||
|
||
{ // Проверяем расширения
|
||
uint32_t count = -1;
|
||
res = vkEnumerateDeviceExtensionProperties(devices[iter], nullptr, &count, nullptr);
|
||
if(res || count == -1)
|
||
{
|
||
error << "Не удалось запросить расширения устройства:\n" << device.getDeviceName() << '\n';
|
||
goto next1;
|
||
} else if(!count)
|
||
goto next1;
|
||
|
||
std::vector<VkExtensionProperties> extensions(count);
|
||
vkEnumerateDeviceExtensionProperties(devices[iter], nullptr, &count, extensions.data());
|
||
device.Extensions.resize(count);
|
||
for(size_t extension = 0; extension < count; extension++)
|
||
{
|
||
device.Extensions[extension].ExtensionName = extensions[extension].extensionName;
|
||
device.Extensions[extension].SpecVersion = extensions[extension].specVersion;
|
||
}
|
||
}
|
||
|
||
next1:
|
||
{ // Проверяем очереди
|
||
uint32_t count = -1;
|
||
vkGetPhysicalDeviceQueueFamilyProperties(devices[iter], &count, nullptr);
|
||
if(count == -1)
|
||
{
|
||
error << "Не удалось запросить очереди устройства:\n" << device.getDeviceName() << '\n';
|
||
goto next2;
|
||
} else if(!count)
|
||
goto next2;
|
||
|
||
std::vector<VkQueueFamilyProperties> deviceQueueProperties(count);
|
||
vkGetPhysicalDeviceQueueFamilyProperties(devices[iter], &count, deviceQueueProperties.data());
|
||
|
||
device.FamilyProperties.resize(count);
|
||
for(size_t family = 0; family < count; family++)
|
||
device.FamilyProperties[family] = deviceQueueProperties[family];
|
||
}
|
||
|
||
next2:
|
||
continue;
|
||
}
|
||
}
|
||
|
||
// Создадим окно
|
||
glfwDefaultWindowHints();
|
||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||
//glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
||
|
||
Graphics.Window = glfwCreateWindow(Screen.Width, Screen.Height, "LuaVox", nullptr, nullptr);
|
||
if (!Graphics.Window)
|
||
{
|
||
const char *error_msg;
|
||
int err_code = glfwGetError(&error_msg);
|
||
error << "Не удалось создать окно GLFW, код: "
|
||
<< err_code << ", ошибка: " << error_msg << '\n';
|
||
goto onError;
|
||
}
|
||
|
||
glfwSetWindowUserPointer(Graphics.Window, this);
|
||
glfwSetFramebufferSizeCallback(Graphics.Window, &glfwCallbackOnResize);
|
||
glfwSetMouseButtonCallback(Graphics.Window, &glfwCallbackOnMouseButton);
|
||
glfwSetCursorPosCallback(Graphics.Window, &glfwCallbackOnCursorPos);
|
||
glfwSetWindowContentScaleCallback(Graphics.Window, &glfwCallbackOnScale);
|
||
glfwSetKeyCallback(Graphics.Window, &glfwCallbackOnKey);
|
||
glfwSetWindowFocusCallback(Graphics.Window, &glfwCallbackOnFocus);
|
||
|
||
VkResult res = glfwCreateWindowSurface(localInstance.getInstance(), Graphics.Window, nullptr, &Graphics.Surface);
|
||
if(res)
|
||
{
|
||
error << "glfwCreateWindowSurface == " << res << '\n';
|
||
goto onError;
|
||
}
|
||
|
||
// Определим наилучшие настройки
|
||
std::vector<int32_t> scores;
|
||
int32_t maxScore = 0;
|
||
vkDevice *devicePtr = nullptr;
|
||
uint32_t dpQueueSurface = -1, dpQueueGraphics = -1;
|
||
|
||
scores.resize(Graphics.Devices.size());
|
||
std::fill(scores.begin(), scores.end(), 0);
|
||
std::stringstream report;
|
||
report << "Отчёт о доступном оборудовании:\n";
|
||
|
||
for(size_t iter = 0; iter < Graphics.Devices.size(); iter++)
|
||
{
|
||
auto &device = Graphics.Devices[iter];
|
||
auto &score = scores[iter];
|
||
|
||
report << std::string(48, '~') << "\n\n" << device.getDeviceName() << '\n';
|
||
|
||
bool canContinue = true;
|
||
|
||
// Необходимый функционал
|
||
if(!device.DeviceFeatures.geometryShader)
|
||
{
|
||
report << "\t*geometryShader: "
|
||
<< device.DeviceFeatures.imageCubeArray
|
||
<< '\n';
|
||
canContinue = false;
|
||
}
|
||
|
||
// Проверка наличия необходимых расширений
|
||
for(auto ext : NeedExtensions)
|
||
if(std::find_if(device.Extensions.begin(), device.Extensions.end(), [&](const vkDeviceExtension &obj) { return obj.ExtensionName == ext; }) == device.Extensions.end())
|
||
{
|
||
report << "\t*Расширение " << ext << ": отсутствует"
|
||
<< '\n';
|
||
canContinue = false;
|
||
}
|
||
|
||
// Дополнительный функционал
|
||
if(!device.DeviceFeatures.fullDrawIndexUint32)
|
||
{
|
||
report << "\tfullDrawIndexUint32: "
|
||
<< device.DeviceFeatures.fullDrawIndexUint32
|
||
<< " : " << device.Limits.maxDrawIndexedIndexValue << '\n';
|
||
} else
|
||
score++;
|
||
|
||
// Поиск очередей
|
||
uint32_t queueSurface = -1, queueGraphics = -1;
|
||
uint32_t value = false;
|
||
|
||
for(size_t queue = 0; queue < device.FamilyProperties.size(); queue++)
|
||
{
|
||
vkGetPhysicalDeviceSurfaceSupportKHR(devices[iter], queue, Graphics.Surface, &value);
|
||
|
||
if(queueSurface == -1 && value == VK_TRUE)
|
||
queueSurface = queue;
|
||
|
||
if(queueGraphics == -1 && (device.FamilyProperties[queue].queueFlags & VK_QUEUE_GRAPHICS_BIT))
|
||
queueGraphics = queue;
|
||
|
||
if(queueSurface != -1 && queueGraphics != -1)
|
||
break;
|
||
}
|
||
|
||
report << "\nQueueSurface: ";
|
||
if(queueSurface == -1)
|
||
{
|
||
report << "очередь не найдена\n";
|
||
canContinue = false;
|
||
} else
|
||
report << queueSurface << '\n';
|
||
|
||
report << "QueueGraphics: ";
|
||
if(queueGraphics == -1)
|
||
{
|
||
report << "очередь не найдена\n";
|
||
canContinue = false;
|
||
} else
|
||
report << queueGraphics << '\n';
|
||
|
||
if(queueSurface != queueGraphics)
|
||
{
|
||
report << "Разные очереди для вывода и рендера не поддерживаются\n";
|
||
canContinue = false;
|
||
}
|
||
|
||
if(!canContinue)
|
||
{
|
||
score = -1;
|
||
report << "Устройство не подходит\n\n";
|
||
continue;
|
||
}
|
||
|
||
// Ограничения
|
||
//device.Limits.
|
||
|
||
report << "\nОценка устройства: " << score << "\n\n";
|
||
if(score > maxScore)
|
||
{
|
||
maxScore = score;
|
||
devicePtr = &device;
|
||
|
||
dpQueueSurface = queueSurface;
|
||
dpQueueGraphics = queueGraphics;
|
||
}
|
||
}
|
||
|
||
LOGGER.debug() << report.str() << std::string(48, '~');
|
||
|
||
if(maxScore == 0 || !devicePtr)
|
||
{
|
||
error << "Не найдено подходящее устройство\n";
|
||
goto onError;
|
||
}
|
||
|
||
// Заполняем наилучшие настройки
|
||
SettingsBest = {{devicePtr->VendorID, devicePtr->DeviceID}, dpQueueGraphics, dpQueueSurface};
|
||
|
||
// Удаляем surface окна по скольку instance будет пересоздан
|
||
if(Graphics.Surface)
|
||
{
|
||
vkDestroySurfaceKHR(localInstance.getInstance(), Graphics.Surface, nullptr);
|
||
Graphics.Surface = nullptr;
|
||
}
|
||
|
||
SettingsState = SettingsBest;
|
||
|
||
} catch(const std::exception &exc) {
|
||
error << "Отловлена глобальная ошибка\n";
|
||
error << exc.what();
|
||
goto onError;
|
||
}
|
||
|
||
if(error.tellp())
|
||
LOGGER.warn() << "Ошибки во время инициализации графической подсистемы:\n" << error.str();
|
||
|
||
return;
|
||
|
||
onError:
|
||
// ДеИнициализация
|
||
try {
|
||
if(Graphics.Window)
|
||
{
|
||
glfwDestroyWindow(Graphics.Window);
|
||
Graphics.Window = nullptr;
|
||
}
|
||
} catch(const std::exception &exc)
|
||
{
|
||
error << "\nОшибки при деинициализации графической подсистемы:\n" << exc.what();
|
||
}
|
||
|
||
MAKE_ERROR("Vulkan: Ошибка инициализации графической подсистемы:\n" << error.str());
|
||
}
|
||
|
||
void Vulkan::initNextSettings()
|
||
{
|
||
if(!SettingsNext.isValid())
|
||
MAKE_ERROR("Невалидные настройки");
|
||
|
||
if(needFullVulkanRebuild())
|
||
{
|
||
deInitVulkan();
|
||
} else {
|
||
for(const std::shared_ptr<IVulkanDependent> &dependent : ROS_Dependents)
|
||
{
|
||
if(/*dependent->needRebuild() != EnumRebuildType::None || */ dynamic_cast<Pipeline*>(dependent.get()))
|
||
dependent->free(this);
|
||
}
|
||
|
||
freeSwapchains();
|
||
}
|
||
|
||
if(!Graphics.Instance)
|
||
{
|
||
std::vector<std::string_view> knownDebugLayers =
|
||
{
|
||
"VK_LAYER_GOOGLE_threading",
|
||
"VK_LAYER_GOOGLE_unique_objects",
|
||
"VK_LAYER_LUNARG_parameter_validation",
|
||
"VK_LAYER_LUNARG_object_tracker",
|
||
"VK_LAYER_LUNARG_image",
|
||
"VK_LAYER_LUNARG_core_validation",
|
||
"VK_LAYER_LUNARG_swapchain",
|
||
"VK_LAYER_LUNARG_standard_validation",
|
||
"VK_LAYER_KHRONOS_validation",
|
||
"VK_LAYER_LUNARG_monitor"
|
||
};
|
||
|
||
if(!SettingsNext.Debug)
|
||
knownDebugLayers.clear();
|
||
|
||
std::vector<vkInstanceLayer> enableDebugLayers;
|
||
|
||
for(auto &name : knownDebugLayers)
|
||
for(const vkInstanceLayer &ext : Graphics.InstanceLayers)
|
||
if(ext.LayerName == name)
|
||
{
|
||
enableDebugLayers.push_back(ext);
|
||
break;
|
||
}
|
||
|
||
Graphics.Instance.emplace(this, enableDebugLayers);
|
||
}
|
||
|
||
if(!Graphics.Surface)
|
||
{
|
||
vkAssert(Graphics.Window);
|
||
VkResult res = glfwCreateWindowSurface(Graphics.Instance->getInstance(), Graphics.Window, nullptr, &Graphics.Surface);
|
||
if(res)
|
||
MAKE_ERROR("glfwCreateWindowSurface: " << res);
|
||
}
|
||
|
||
// Найдём PhysicalDevice
|
||
if(!Graphics.PhysicalDevice) //Graphics.PhysicalDevice = nullptr;
|
||
{
|
||
uint32_t count = -1;
|
||
VkResult res;
|
||
|
||
vkAssert(!vkEnumeratePhysicalDevices(Graphics.Instance->getInstance(), &count, nullptr));
|
||
if(!count)
|
||
MAKE_ERROR("vkEnumeratePhysicalDevices сообщил об отсутствии подходящего устройства");
|
||
|
||
std::vector<VkPhysicalDevice> devices(count);
|
||
vkAssert(!vkEnumeratePhysicalDevices(Graphics.Instance->getInstance(), &count, devices.data()));
|
||
for(size_t iter = 0; iter < count; iter++)
|
||
{
|
||
VkPhysicalDeviceProperties deviceProperties;
|
||
vkGetPhysicalDeviceProperties(devices[iter], &deviceProperties);
|
||
|
||
if(deviceProperties.vendorID == SettingsNext.DeviceMain.VendorId
|
||
&& deviceProperties.deviceID == SettingsNext.DeviceMain.DeviceId)
|
||
{
|
||
Graphics.PhysicalDevice = devices[iter];
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if(!Graphics.PhysicalDevice)
|
||
MAKE_ERROR("Требуемое устройство не найдено, VendorId: "
|
||
<< SettingsNext.DeviceMain.VendorId << ", DeviceId: "
|
||
<< SettingsNext.DeviceMain.DeviceId);
|
||
|
||
// Создаём виртуальное устройство
|
||
// TODO: добавить поддержку разделённых очередей
|
||
if(!Graphics.Device)
|
||
{
|
||
float queue_priorities[1] = { 0.0 };
|
||
const VkDeviceQueueCreateInfo infoQueue =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.queueFamilyIndex = SettingsNext.QueueGraphics,
|
||
.queueCount = 1,
|
||
.pQueuePriorities = queue_priorities
|
||
};
|
||
|
||
VkPhysicalDeviceVulkan11Features feat11;
|
||
|
||
memset(&feat11, 0, sizeof(feat11));
|
||
feat11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
|
||
|
||
VkPhysicalDeviceFeatures2 features = {
|
||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||
.pNext = &feat11,
|
||
.features = {0}
|
||
};
|
||
|
||
features.features.geometryShader = true;
|
||
|
||
feat11.uniformAndStorageBuffer16BitAccess = true;
|
||
feat11.storageBuffer16BitAccess = true;
|
||
|
||
VkDeviceCreateInfo infoDevice =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||
.pNext = &features,
|
||
.flags = 0,
|
||
.queueCreateInfoCount = 1,
|
||
.pQueueCreateInfos = &infoQueue,
|
||
.enabledLayerCount = 0,
|
||
.ppEnabledLayerNames = nullptr,
|
||
.enabledExtensionCount = (uint32_t) NeedExtensions.size(),
|
||
.ppEnabledExtensionNames = (const char* const*) NeedExtensions.data(),
|
||
.pEnabledFeatures = nullptr
|
||
};
|
||
|
||
vkAssert(!vkCreateDevice(Graphics.PhysicalDevice, &infoDevice, nullptr, &Graphics.Device));
|
||
vkGetDeviceQueue(Graphics.Device, SettingsNext.QueueGraphics, 0, &Graphics.DeviceQueueGraphic);
|
||
}
|
||
|
||
// Определяемся с форматом экранного буфера
|
||
uint32_t count = -1;
|
||
vkAssert(!vkGetPhysicalDeviceSurfaceFormatsKHR(Graphics.PhysicalDevice, Graphics.Surface, &count, VK_NULL_HANDLE));
|
||
std::vector<VkSurfaceFormatKHR> surfFormats(count);
|
||
vkAssert(!vkGetPhysicalDeviceSurfaceFormatsKHR(Graphics.PhysicalDevice, Graphics.Surface, &count, surfFormats.data()));
|
||
vkAssert(surfFormats.size());
|
||
|
||
if (count == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) {
|
||
Graphics.SurfaceFormat = VK_FORMAT_R32G32B32_UINT;
|
||
Graphics.SurfaceColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||
|
||
LOGGER.debug() << "Формат экранного буфера не определён устройством, используется: VK_FORMAT_B8G8R8A8_UNORM & VK_COLOR_SPACE_SRGB_NONLINEAR_KHR";
|
||
|
||
} else {
|
||
vkAssert(count >= 1 && "Отсутствуют подходящие форматы экранного буфера vkGetPhysicalDeviceSurfaceFormatsKHR");
|
||
|
||
bool find = false;
|
||
for(size_t iter = 0; iter < count; iter++)
|
||
if(surfFormats[iter].format == VK_FORMAT_B8G8R8A8_UNORM
|
||
&& surfFormats[iter].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
|
||
{
|
||
Graphics.SurfaceFormat = VK_FORMAT_B8G8R8A8_UNORM;
|
||
Graphics.SurfaceColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||
find = true;
|
||
LOGGER.debug() << "Формат экранного буфера по умолчанию: VK_FORMAT_B8G8R8A8_UNORM & VK_COLOR_SPACE_SRGB_NONLINEAR_KHR";
|
||
break;
|
||
}
|
||
|
||
if(!find)
|
||
{
|
||
LOGGER.debug() << "Не найден формат экранного буфера VK_FORMAT_B8G8R8A8_UNORM & VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, используется "
|
||
<< int(surfFormats[0].format) << " & " << int(surfFormats[0].colorSpace);
|
||
Graphics.SurfaceFormat = surfFormats[0].format;
|
||
Graphics.SurfaceColorSpace = surfFormats[0].colorSpace;
|
||
}
|
||
}
|
||
|
||
// Запрос настроек памяти для данного устройства
|
||
vkGetPhysicalDeviceMemoryProperties(Graphics.PhysicalDevice, &Graphics.DeviceMemoryProperties);
|
||
|
||
// Создание пула команд (видимо для одной очереди)
|
||
if(!Graphics.Pool)
|
||
{
|
||
const VkCommandPoolCreateInfo infoCmdPool =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||
.queueFamilyIndex = SettingsNext.QueueGraphics
|
||
};
|
||
|
||
vkAssert(!vkCreateCommandPool(Graphics.Device, &infoCmdPool, nullptr, &Graphics.Pool));
|
||
}
|
||
|
||
// Создание буферов команд (подготовка данных CommandBufferData, рендер CommandBufferRender)
|
||
{
|
||
const VkCommandBufferAllocateInfo infoCmd =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||
.pNext = nullptr,
|
||
.commandPool = Graphics.Pool,
|
||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||
.commandBufferCount = 1
|
||
};
|
||
|
||
if(!Graphics.CommandBufferData)
|
||
{
|
||
vkAssert(!vkAllocateCommandBuffers(Graphics.Device, &infoCmd, &Graphics.CommandBufferData));
|
||
|
||
// Старт очереди команд
|
||
VkCommandBufferBeginInfo infoCmdBuffer =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.pInheritanceInfo = nullptr
|
||
};
|
||
|
||
vkAssert(!vkBeginCommandBuffer(Graphics.CommandBufferData, &infoCmdBuffer));
|
||
}
|
||
|
||
if(!Graphics.CommandBufferRender)
|
||
vkAssert(!vkAllocateCommandBuffers(Graphics.Device, &infoCmd, &Graphics.CommandBufferRender));
|
||
}
|
||
|
||
// Создание RenderPass для экранного буфера
|
||
if(!Graphics.RenderPass)
|
||
{
|
||
const VkAttachmentDescription attachments[2] =
|
||
{
|
||
{
|
||
.flags = 0,
|
||
.format = Graphics.SurfaceFormat,
|
||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||
.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||
.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
||
}, {
|
||
.flags = 0,
|
||
.format = Graphics.DepthFormat,
|
||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||
.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||
.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
||
.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
|
||
}
|
||
};
|
||
|
||
const VkAttachmentReference referenceColor1 =
|
||
{
|
||
.attachment = 0,
|
||
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
||
};
|
||
const VkAttachmentReference referenceDepth =
|
||
{
|
||
.attachment = 1,
|
||
.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
|
||
};
|
||
|
||
const VkSubpassDescription subpass[1] =
|
||
{
|
||
{
|
||
.flags = 0,
|
||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||
.inputAttachmentCount = 0,
|
||
.pInputAttachments = nullptr,
|
||
.colorAttachmentCount = 1,
|
||
.pColorAttachments = &referenceColor1,
|
||
.pResolveAttachments = nullptr,
|
||
.pDepthStencilAttachment = &referenceDepth,
|
||
.preserveAttachmentCount = 0,
|
||
.pPreserveAttachments = nullptr
|
||
}
|
||
};
|
||
|
||
// Зависимости между подпроходами
|
||
// const VkSubpassDependency rp_info_dep[2] =
|
||
// {
|
||
// {
|
||
// .srcSubpass = VK_SUBPASS_EXTERNAL,
|
||
// .dstSubpass = 0,
|
||
// .srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||
// .dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||
// .srcAccessMask = 0,
|
||
// .dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT |
|
||
// VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
|
||
// VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||
// VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
|
||
// VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
||
// .dependencyFlags = 0
|
||
// },
|
||
// {
|
||
// .srcSubpass = 0,
|
||
// .dstSubpass = 1,
|
||
// .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||
// .dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||
// .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||
// .dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT,
|
||
// .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT
|
||
// },
|
||
// };
|
||
|
||
const VkRenderPassCreateInfo rp_info =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.attachmentCount = 2,
|
||
.pAttachments = attachments,
|
||
.subpassCount = 1,
|
||
.pSubpasses = subpass,
|
||
.dependencyCount = 0,
|
||
.pDependencies = nullptr
|
||
};
|
||
|
||
vkAssert(!vkCreateRenderPass(Graphics.Device, &rp_info, nullptr, &Graphics.RenderPass));
|
||
}
|
||
|
||
// Цепочки рендера
|
||
buildSwapchains();
|
||
|
||
// Пул дескрипторов для ImGui
|
||
#ifdef HAS_IMGUI
|
||
if(!Graphics.ImGuiDescPool)
|
||
{
|
||
VkDescriptorPoolSize pool_sizes[] =
|
||
{
|
||
{ VK_DESCRIPTOR_TYPE_SAMPLER, 1024 },
|
||
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1024 },
|
||
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1024 },
|
||
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1024 },
|
||
{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1024 },
|
||
{ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1024 },
|
||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1024 },
|
||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1024 },
|
||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1024 },
|
||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1024 },
|
||
{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1024 }
|
||
};
|
||
|
||
VkDescriptorPoolCreateInfo descriptor_pool = {};
|
||
descriptor_pool.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||
descriptor_pool.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
|
||
descriptor_pool.maxSets = 1024;
|
||
descriptor_pool.poolSizeCount = (uint32_t) IM_ARRAYSIZE(pool_sizes);
|
||
descriptor_pool.pPoolSizes = pool_sizes;
|
||
|
||
vkAssert(!vkCreateDescriptorPool(Graphics.Device, &descriptor_pool, nullptr, &Graphics.ImGuiDescPool));
|
||
|
||
ImGui::CreateContext();
|
||
|
||
ImGui::StyleColorsDark();
|
||
ImGuiIO& io = ImGui::GetIO();
|
||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
||
io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
|
||
|
||
ImGui_ImplGlfw_InitForVulkan(Graphics.Window, true);
|
||
|
||
ImGui_ImplVulkan_InitInfo ImGuiInfo =
|
||
{
|
||
.Instance = Graphics.Instance->getInstance(),
|
||
.PhysicalDevice = Graphics.PhysicalDevice,
|
||
.Device = Graphics.Device,
|
||
.QueueFamily = SettingsNext.QueueGraphics,
|
||
.Queue = Graphics.DeviceQueueGraphic,
|
||
.DescriptorPool = Graphics.ImGuiDescPool,
|
||
.RenderPass = Graphics.RenderPass,
|
||
.MinImageCount = Graphics.DrawBufferCount,
|
||
.ImageCount = Graphics.DrawBufferCount,
|
||
.MSAASamples = VK_SAMPLE_COUNT_1_BIT,
|
||
.PipelineCache = nullptr,
|
||
.Subpass = 0,
|
||
.UseDynamicRendering = false,
|
||
.PipelineRenderingCreateInfo = {},
|
||
.Allocator= nullptr,
|
||
.CheckVkResultFn = check_vk_result,
|
||
.MinAllocationSize = 1024*1024
|
||
};
|
||
|
||
ImGui_ImplVulkan_Init(&ImGuiInfo);
|
||
|
||
// ImFontConfig fontConfig;
|
||
// fontConfig.MergeMode = false;
|
||
// fontConfig.PixelSnapH = true;
|
||
// fontConfig.OversampleH = 1;
|
||
// fontConfig.OversampleV = 1;
|
||
|
||
// Buffer<byte> fontFile = File("assets/client/fonts/UZSans-Bold.ttf").openReader()->readAsBuffer();
|
||
// byte *fontPtr = new byte[fontFile.getSize()];
|
||
// std::copy(fontFile.getData(), fontFile.getDataBack()+1, fontPtr);
|
||
// try{
|
||
// io.Fonts->AddFontFromMemoryTTF(fontPtr, fontFile.getSize(), 16.0f, &fontConfig, io.Fonts->GetGlyphRangesCyrillic());
|
||
// } catch(...) {
|
||
// delete[] fontPtr;
|
||
// throw;
|
||
// }
|
||
// //io.Fonts->AddFontFromFileTTF("assets/client/fonts/UZSans-Bold.ttf", 16.0f, &fontConfig, io.Fonts->GetGlyphRangesCyrillic());
|
||
// //io.Fonts->AddFontDefault(&fontConfig);
|
||
|
||
// ImGui_ImplVulkan_CreateFontsTexture(CBSetup);
|
||
// flushInitCMD();
|
||
// ImGui_ImplVulkan_DestroyFontUploadObjects();
|
||
}
|
||
|
||
#endif
|
||
|
||
for(const std::shared_ptr<IVulkanDependent> &dependent : ROS_Dependents)
|
||
dependent->init(this);
|
||
|
||
LOGGER.debug() << Graphics.SwapchainChangeReport;
|
||
Graphics.SwapchainChangeReport = "";
|
||
|
||
SettingsState = SettingsNext;
|
||
}
|
||
|
||
void Vulkan::deInitVulkan()
|
||
{
|
||
#ifdef HAS_IMGUI
|
||
if(ImGui::GetCurrentContext())
|
||
{
|
||
ImGui_ImplVulkan_Shutdown();
|
||
ImGui_ImplGlfw_Shutdown();
|
||
ImGui::DestroyContext();
|
||
}
|
||
#endif
|
||
|
||
for(std::shared_ptr<IVulkanDependent> dependent : ROS_Dependents)
|
||
dependent->free(this);
|
||
|
||
freeSwapchains();
|
||
|
||
{
|
||
std::lock_guard locK(Screen.BeforeDrawMtx);
|
||
while(!Screen.BeforeDraw.empty())
|
||
{
|
||
Screen.BeforeDraw.front()(this);
|
||
Screen.BeforeDraw.pop();
|
||
}
|
||
}
|
||
|
||
if(Graphics.Instance)
|
||
{
|
||
if(Graphics.Device)
|
||
{
|
||
if(Graphics.ImGuiDescPool)
|
||
vkDestroyDescriptorPool(Graphics.Device, Graphics.ImGuiDescPool, nullptr);
|
||
|
||
// Удаляем SwapChain
|
||
if(Graphics.Swapchain)
|
||
vkDestroySwapchainKHR(Graphics.Device, Graphics.Swapchain, nullptr);
|
||
|
||
// Очистка буферов команд
|
||
if(Graphics.CommandBufferData)
|
||
vkFreeCommandBuffers(Graphics.Device, Graphics.Pool, 1, &Graphics.CommandBufferData);
|
||
|
||
if(Graphics.CommandBufferRender)
|
||
vkFreeCommandBuffers(Graphics.Device, Graphics.Pool, 1, &Graphics.CommandBufferRender);
|
||
|
||
// Освобождение пула команд
|
||
if(Graphics.Pool)
|
||
vkDestroyCommandPool(Graphics.Device, Graphics.Pool, nullptr);
|
||
|
||
// Удаляем RenderPass
|
||
if(Graphics.RenderPass)
|
||
vkDestroyRenderPass(Graphics.Device, Graphics.RenderPass, nullptr);
|
||
|
||
// Освобождение виртуального устройства
|
||
//if(Graphics.Device)
|
||
// vkDestroyDevice(Graphics.Device, nullptr);
|
||
}
|
||
|
||
if(Graphics.Surface)
|
||
vkDestroySurfaceKHR(Graphics.Instance->getInstance(), Graphics.Surface, nullptr);
|
||
}
|
||
|
||
Graphics.ImGuiDescPool = nullptr;
|
||
Graphics.Swapchain = nullptr;
|
||
Graphics.CommandBufferData = nullptr;
|
||
Graphics.CommandBufferRender = nullptr;
|
||
Graphics.Pool = nullptr;
|
||
Graphics.RenderPass = nullptr;
|
||
Graphics.Device = nullptr;
|
||
Graphics.PhysicalDevice = nullptr;
|
||
Graphics.DeviceQueueGraphic = nullptr;
|
||
Graphics.Surface = nullptr;
|
||
Graphics.Instance.reset();
|
||
}
|
||
|
||
void Vulkan::flushCommandBufferData()
|
||
{
|
||
vkAssert(!vkEndCommandBuffer(Graphics.CommandBufferData));
|
||
|
||
const VkCommandBuffer cmd_bufs[] = { Graphics.CommandBufferData };
|
||
VkFence nullFence = { VK_NULL_HANDLE };
|
||
VkSubmitInfo submit_info =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||
.pNext = nullptr,
|
||
.waitSemaphoreCount = 0,
|
||
.pWaitSemaphores = nullptr,
|
||
.pWaitDstStageMask = nullptr,
|
||
.commandBufferCount = 1,
|
||
.pCommandBuffers = cmd_bufs,
|
||
.signalSemaphoreCount = 0,
|
||
.pSignalSemaphores = nullptr
|
||
};
|
||
|
||
vkAssert(!vkQueueSubmit(Graphics.DeviceQueueGraphic, 1, &submit_info, nullFence));
|
||
vkAssert(!vkQueueWaitIdle(Graphics.DeviceQueueGraphic));
|
||
|
||
VkCommandBufferBeginInfo infoCmdBuffer =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.pInheritanceInfo = nullptr
|
||
};
|
||
|
||
vkAssert(!vkBeginCommandBuffer(Graphics.CommandBufferData, &infoCmdBuffer));
|
||
}
|
||
|
||
Settings Vulkan::getBestSettings()
|
||
{
|
||
checkLibrary();
|
||
return SettingsBest;
|
||
}
|
||
|
||
void Vulkan::reInit()
|
||
{
|
||
checkLibrary();
|
||
initNextSettings();
|
||
addImGUIFont(LV::getResource("default.ttf")->makeView());
|
||
}
|
||
|
||
bool Vulkan::needFullVulkanRebuild()
|
||
{
|
||
return false;
|
||
}
|
||
|
||
std::shared_ptr<ShaderModule> Vulkan::createShader(std::string_view data)
|
||
{
|
||
vkAssert(Graphics.Device);
|
||
vkAssert(data.size());
|
||
std::shared_ptr<ShaderModule> module = std::make_shared<ShaderModule>(data);
|
||
std::dynamic_pointer_cast<IVulkanDependent>(module)->init(this);
|
||
ROS_Dependents.insert(module);
|
||
return module;
|
||
}
|
||
|
||
void Vulkan::registerDependent(std::shared_ptr<IVulkanDependent> dependent)
|
||
{
|
||
vkAssert(Graphics.Device);
|
||
dependent->init(this);
|
||
ROS_Dependents.insert(dependent);
|
||
}
|
||
|
||
Vulkan& Vulkan::operator<<(std::shared_ptr<IVulkanDependent> dependent)
|
||
{
|
||
registerDependent(dependent);
|
||
return *this;
|
||
}
|
||
|
||
void Vulkan::addImGUIFont(std::string_view view) {
|
||
ImFontConfig fontConfig;
|
||
fontConfig.MergeMode = false;
|
||
fontConfig.PixelSnapH = true;
|
||
fontConfig.OversampleH = 1;
|
||
fontConfig.OversampleV = 1;
|
||
|
||
auto &io = ImGui::GetIO();
|
||
uint8_t *fontPtr = (uint8_t*) malloc(view.size());
|
||
if(!fontPtr)
|
||
MAKE_ERROR("Not enough memory");
|
||
|
||
std::copy(view.begin(), view.end(), fontPtr);
|
||
try{
|
||
io.Fonts->AddFontFromMemoryTTF(fontPtr, view.size(), 16.0f, &fontConfig, io.Fonts->GetGlyphRangesCyrillic());
|
||
} catch(...) {
|
||
free(fontPtr);
|
||
throw;
|
||
}
|
||
}
|
||
|
||
void Vulkan::gui_MainMenu() {
|
||
if(!ImGui::Begin("MainMenu", nullptr, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove))
|
||
return;
|
||
|
||
static struct {
|
||
char Address[256] = "localhost", Username[256], Password[256];
|
||
bool Cancel = false, InProgress = false;
|
||
std::string Progress;
|
||
|
||
std::unique_ptr<Net::AsyncSocket> Socket;
|
||
|
||
coro<> connect(asio::io_context &ioc) {
|
||
try {
|
||
std::string a(Address, strlen(Address));
|
||
std::string u(Username, strlen(Username));
|
||
std::string p(Password, strlen(Password));
|
||
|
||
tcp::socket sock = co_await Net::asyncConnectTo(a, [&](const std::string &text) {
|
||
Progress += text;
|
||
});
|
||
|
||
co_await Client::ServerSession::asyncAuthorizeWithServer(sock, u, p, 1, [&](const std::string &text) {
|
||
Progress += text;
|
||
});
|
||
|
||
Socket = co_await Client::ServerSession::asyncInitGameProtocol(ioc, std::move(sock), [&](const std::string &text) {
|
||
Progress += text;
|
||
});
|
||
} catch(const std::exception &exc) {
|
||
Progress += "\n-> ";
|
||
Progress += exc.what();
|
||
}
|
||
|
||
InProgress = false;
|
||
|
||
co_return;
|
||
}
|
||
} ConnectionProgress;
|
||
|
||
ImGui::InputText("Address", ConnectionProgress.Address, sizeof(ConnectionProgress.Address));
|
||
ImGui::InputText("Username", ConnectionProgress.Username, sizeof(ConnectionProgress.Username));
|
||
ImGui::InputText("Password", ConnectionProgress.Password, sizeof(ConnectionProgress.Password), ImGuiInputTextFlags_Password);
|
||
|
||
if(!ConnectionProgress.InProgress && !ConnectionProgress.Socket) {
|
||
if(ImGui::Button("Подключиться")) {
|
||
ConnectionProgress.InProgress = true;
|
||
ConnectionProgress.Cancel = false;
|
||
ConnectionProgress.Progress.clear();
|
||
asio::co_spawn(IOC, ConnectionProgress.connect(IOC), asio::detached);
|
||
}
|
||
|
||
if(!Game.Server) {
|
||
if(ImGui::Button("Запустить сервер")) {
|
||
try {
|
||
Game.Server = std::make_unique<ServerObj>(IOC);
|
||
ConnectionProgress.Progress = "Сервер запущен на порту " + std::to_string(Game.Server->LS.getPort());
|
||
} catch(const std::exception &exc) {
|
||
ConnectionProgress.Progress = "Не удалось запустить внутренний сервер: " + std::string(exc.what());
|
||
}
|
||
}
|
||
} else {
|
||
if(!Game.Server->GS.isAlive())
|
||
Game.Server = nullptr;
|
||
else if(ImGui::Button("Остановить сервер")) {
|
||
Game.Server->GS.shutdown("Сервер останавливается по запросу интерфейса");
|
||
}
|
||
}
|
||
}
|
||
|
||
if(ConnectionProgress.InProgress) {
|
||
if(ImGui::Button("Отмена"))
|
||
ConnectionProgress.Cancel = true;
|
||
}
|
||
|
||
if(!ConnectionProgress.Progress.empty()) {
|
||
if(ImGui::BeginChild("Прогресс", {0, 0}, ImGuiChildFlags_Borders, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_HorizontalScrollbar)) {
|
||
ImGui::Text("Прогресс:\n%s", ConnectionProgress.Progress.c_str());
|
||
ImGui::EndChild();
|
||
}
|
||
}
|
||
|
||
if(ConnectionProgress.Socket) {
|
||
std::unique_ptr<Net::AsyncSocket> sock = std::move(ConnectionProgress.Socket);
|
||
Game.RSession = std::make_unique<VulkanRenderSession>();
|
||
*this << Game.RSession;
|
||
Game.Session = std::make_unique<ServerSession>(IOC, std::move(sock), Game.RSession.get());
|
||
Game.RSession->setServerSession(Game.Session.get());
|
||
Game.ImGuiInterfaces.push_back(&Vulkan::gui_ConnectedToServer);
|
||
}
|
||
|
||
|
||
ImGui::End();
|
||
}
|
||
|
||
void Vulkan::gui_ConnectedToServer() {
|
||
if(Game.Session) {
|
||
if(Game.Session->isConnected())
|
||
return;
|
||
|
||
Game.RSession = nullptr;
|
||
Game.Session = nullptr;
|
||
Game.ImGuiInterfaces.pop_back();
|
||
|
||
int mode = glfwGetInputMode(Graphics.Window, GLFW_CURSOR);
|
||
if(mode == GLFW_CURSOR_DISABLED)
|
||
glfwSetInputMode(Graphics.Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||
}
|
||
}
|
||
|
||
|
||
IVulkanDependent::~IVulkanDependent() = default;
|
||
|
||
void Vulkan::updateResources()
|
||
{
|
||
// for(const std::shared_ptr<IVulkanDependent> &dependent : ROS_Dependents)
|
||
// {
|
||
// if(dependent->needRebuild() != EnumRebuildType::None)
|
||
// {
|
||
// dependent->free(this);
|
||
// dependent->init(this);
|
||
// }
|
||
// }
|
||
}
|
||
|
||
|
||
// --------------- vkInstance ---------------
|
||
|
||
|
||
Vulkan::vkInstance::vkInstance(Vulkan *handler, std::vector<vkInstanceLayer> layers, std::vector<vkInstanceExtension> extensions)
|
||
: Handler(handler)
|
||
{
|
||
vkAssert(handler);
|
||
//TOS_vkAssert(getShared(), "Должен быть Shared");
|
||
|
||
size_t glfwExtensions = handler->Graphics.GLFWExtensions.size();
|
||
std::vector<const char*> rawLayers(layers.size()), rawExtensions(extensions.size()+glfwExtensions);
|
||
|
||
// Подготавливаем слои
|
||
{
|
||
const char **ptrTo = rawLayers.data();
|
||
const vkInstanceLayer *ptrFrom = layers.data();
|
||
|
||
for(size_t iter = 0; iter < layers.size(); iter++, ptrFrom++, ptrTo++)
|
||
*ptrTo = ptrFrom->LayerName.c_str();
|
||
}
|
||
|
||
// Подготавливаем расширения
|
||
{
|
||
const char **ptrTo = rawExtensions.data();
|
||
const vkInstanceExtension *ptrFrom = extensions.data();
|
||
|
||
size_t iter = 0;
|
||
for(iter = 0; iter < extensions.size(); iter++, ptrFrom++, ptrTo++)
|
||
*ptrTo = ptrFrom->ExtensionName.c_str();
|
||
|
||
for(; iter < glfwExtensions + extensions.size(); iter++, ptrTo++)
|
||
*ptrTo = handler->Graphics.GLFWExtensions[iter-extensions.size()].c_str();
|
||
}
|
||
|
||
const VkApplicationInfo infoApp =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||
.pNext = nullptr,
|
||
.pApplicationName = "NFB_DUCKOUT",
|
||
.applicationVersion = 1,
|
||
.pEngineName = "NFB_DUCKOUT",
|
||
.engineVersion = 1,
|
||
.apiVersion = VK_API_VERSION_1_2
|
||
};
|
||
|
||
VkInstanceCreateInfo infoInstance =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.pApplicationInfo = &infoApp,
|
||
.enabledLayerCount = (uint32_t) rawLayers.size(),
|
||
.ppEnabledLayerNames = (const char* const*) rawLayers.data(),
|
||
.enabledExtensionCount = (uint32_t) rawExtensions.size(),
|
||
.ppEnabledExtensionNames = (const char* const*) rawExtensions.data()
|
||
};
|
||
|
||
//if (portability_enumeration)
|
||
// inst_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
||
|
||
VkResult err = vkCreateInstance(&infoInstance, nullptr, &Instance);
|
||
if (err == VK_ERROR_INCOMPATIBLE_DRIVER)
|
||
MAKE_ERROR("Не найден подходящий драйвер клиента Vulkan (ICD)");
|
||
else if (err == VK_ERROR_LAYER_NOT_PRESENT)
|
||
MAKE_ERROR("Не удалось найти требуемые слои библиотеки-клиента Vulkan (ICD)");
|
||
else if (err == VK_ERROR_EXTENSION_NOT_PRESENT)
|
||
MAKE_ERROR("Не удалось найти требуемые расширения библиотеки-клиента Vulkan (ICD)");
|
||
else if (err)
|
||
MAKE_ERROR("Не удалось создать VulkanInstance по неизвестной ошибке");
|
||
}
|
||
|
||
Vulkan::vkInstance::~vkInstance()
|
||
{
|
||
if(!Instance)
|
||
return;
|
||
|
||
//vkDestroyInstance(Instance, nullptr);
|
||
}
|
||
|
||
|
||
// --------------- DescriptorLayout--------------
|
||
|
||
/*DescriptorLayout::DescriptorLayout()
|
||
{
|
||
// Информация о дескрипторах
|
||
ShaderLayoutBindings =
|
||
{
|
||
{
|
||
.binding = 0,
|
||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||
.descriptorCount = 1,
|
||
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||
.pImmutableSamplers = nullptr
|
||
}, {
|
||
.binding = 1,
|
||
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||
.descriptorCount = 1,
|
||
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
|
||
.pImmutableSamplers = nullptr
|
||
}
|
||
};
|
||
|
||
Settings.ShaderPushConstants =
|
||
{
|
||
{
|
||
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
|
||
.offset = 0,
|
||
.size = 204 //uint32_t(sizeof(PipelineWorld.UBO))
|
||
}
|
||
};
|
||
}*/
|
||
|
||
DescriptorLayout::DescriptorLayout(const std::vector<VkDescriptorSetLayoutBinding> &layout, const std::vector<VkPushConstantRange> &pushConstants)
|
||
: ShaderLayoutBindings(layout), ShaderPushConstants(pushConstants)
|
||
{}
|
||
|
||
DescriptorLayout::~DescriptorLayout() = default;
|
||
|
||
void DescriptorLayout::init(Vulkan *instance)
|
||
{
|
||
if(!DescLayout)
|
||
{
|
||
const VkDescriptorSetLayoutCreateInfo descriptor_layout =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.bindingCount = (uint32_t) ShaderLayoutBindings.size(),
|
||
.pBindings = ShaderLayoutBindings.data()
|
||
};
|
||
|
||
vkAssert(!vkCreateDescriptorSetLayout(instance->Graphics.Device, &descriptor_layout, nullptr, &DescLayout));
|
||
}
|
||
|
||
if(!Layout)
|
||
{
|
||
// Дескрипторы в шейдере
|
||
const VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.setLayoutCount = 1,
|
||
.pSetLayouts = &DescLayout,
|
||
.pushConstantRangeCount = (uint32_t) ShaderPushConstants.size(),
|
||
.pPushConstantRanges = ShaderPushConstants.data()
|
||
};
|
||
|
||
vkAssert(!vkCreatePipelineLayout(instance->Graphics.Device, &pPipelineLayoutCreateInfo, nullptr, &Layout));
|
||
}
|
||
}
|
||
|
||
void DescriptorLayout::free(Vulkan *instance)
|
||
{
|
||
if(instance->Graphics.Device)
|
||
{
|
||
if(DescLayout)
|
||
vkDestroyDescriptorSetLayout(instance->Graphics.Device, DescLayout, nullptr);
|
||
|
||
if(Layout)
|
||
vkDestroyPipelineLayout(instance->Graphics.Device, Layout, nullptr);
|
||
}
|
||
|
||
DescLayout = nullptr;
|
||
Layout = nullptr;
|
||
}
|
||
|
||
|
||
|
||
// --------------- Pipeline ---------------
|
||
|
||
|
||
|
||
Pipeline::Pipeline(std::shared_ptr<DescriptorLayout> layout)
|
||
{
|
||
vkAssert(layout);
|
||
Settings.ShaderLayoutBindings = layout;
|
||
|
||
// Информация о буферах и размере вершины
|
||
Settings.ShaderVertexBindings =
|
||
{
|
||
{
|
||
.binding = 0,
|
||
.stride = sizeof(uint32_t),
|
||
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX
|
||
}
|
||
};
|
||
|
||
// Парсинг данных из буферов
|
||
Settings.ShaderVertexAttribute =
|
||
{
|
||
{
|
||
.location = 0,
|
||
.binding = 0,
|
||
.format = VK_FORMAT_R32_UINT,
|
||
.offset = 0
|
||
}
|
||
};
|
||
|
||
Settings.ShaderStages =
|
||
{
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
||
.module = nullptr, //vertexShader.getModule(),
|
||
.pName = "main",
|
||
.pSpecializationInfo = nullptr
|
||
}, {
|
||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||
.module = nullptr, //fragmentShader.getModule(),
|
||
.pName = "main",
|
||
.pSpecializationInfo = nullptr
|
||
}
|
||
};
|
||
|
||
Settings.Rasterization = VkPipelineRasterizationStateCreateInfo
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.depthClampEnable = false,
|
||
.rasterizerDiscardEnable = false,
|
||
.polygonMode = VK_POLYGON_MODE_FILL,
|
||
.cullMode = VK_CULL_MODE_BACK_BIT,
|
||
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
|
||
.depthBiasEnable = false,
|
||
.depthBiasConstantFactor = 0.0f,
|
||
.depthBiasClamp = 0.0f,
|
||
.depthBiasSlopeFactor = 0.0f,
|
||
.lineWidth = 1.0f
|
||
};
|
||
|
||
Settings.Multisample = VkPipelineMultisampleStateCreateInfo
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
|
||
.sampleShadingEnable = false,
|
||
.minSampleShading = 0.0f,
|
||
.pSampleMask = nullptr,
|
||
.alphaToCoverageEnable = false,
|
||
.alphaToOneEnable = false
|
||
};
|
||
|
||
Settings.ColorBlend =
|
||
{
|
||
{
|
||
.blendEnable = false,
|
||
.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE,
|
||
.colorBlendOp = VK_BLEND_OP_ADD,
|
||
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||
.alphaBlendOp = VK_BLEND_OP_ADD,
|
||
.colorWriteMask = 0xf
|
||
}
|
||
};
|
||
|
||
for(int i = 0; i < 4; i++)
|
||
Settings.ColorBlendConstants[i] = 0.0f;
|
||
Settings.ColorBlendLogicOp = VK_LOGIC_OP_CLEAR;
|
||
|
||
Settings.DepthStencil = VkPipelineDepthStencilStateCreateInfo
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.depthTestEnable = true,
|
||
.depthWriteEnable = true,
|
||
.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL,
|
||
.depthBoundsTestEnable = false,
|
||
.stencilTestEnable = false,
|
||
.front = VkStencilOpState
|
||
{
|
||
.failOp = VK_STENCIL_OP_KEEP,
|
||
.passOp = VK_STENCIL_OP_KEEP,
|
||
.depthFailOp = VK_STENCIL_OP_KEEP,
|
||
.compareOp = VK_COMPARE_OP_ALWAYS,
|
||
.compareMask = 0x0,
|
||
.writeMask = 0x0,
|
||
.reference = 0x0
|
||
},
|
||
.back = Settings.DepthStencil.front,
|
||
.minDepthBounds = 0.0f,
|
||
.maxDepthBounds = 0.0f
|
||
};
|
||
|
||
Settings.DepthStencil.back = Settings.DepthStencil.front;
|
||
|
||
Settings.DynamicStates = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
|
||
}
|
||
|
||
Pipeline::~Pipeline() = default;
|
||
|
||
void Pipeline::free(Vulkan *instance)
|
||
{
|
||
if(instance && instance->Graphics.Device)
|
||
{
|
||
if(PipelineObj)
|
||
vkDestroyPipeline(instance->Graphics.Device, PipelineObj, nullptr);
|
||
}
|
||
|
||
PipelineObj = nullptr;
|
||
}
|
||
|
||
void Pipeline::init(Vulkan *instance)
|
||
{
|
||
vkAssert(instance);
|
||
vkAssert(Settings.ShaderLayoutBindings && Settings.ShaderLayoutBindings->DescLayout);
|
||
|
||
// Топология вершин на входе (треугольники, линии, точки)
|
||
VkPipelineInputAssemblyStateCreateInfo ia =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.topology = Topology,
|
||
.primitiveRestartEnable = false
|
||
};
|
||
|
||
VkPipelineViewportStateCreateInfo vp =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.viewportCount = 1,
|
||
.pViewports = nullptr,
|
||
.scissorCount = 1,
|
||
.pScissors = nullptr
|
||
};
|
||
|
||
// Настройки конвейера, которые могут быть изменены без пересоздания конвейера
|
||
VkPipelineDynamicStateCreateInfo dynamicState =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.dynamicStateCount = (uint32_t) Settings.DynamicStates.size(),
|
||
.pDynamicStates = Settings.DynamicStates.data(),
|
||
};
|
||
|
||
// Логика смешивания цветов
|
||
VkPipelineColorBlendStateCreateInfo cb =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.logicOpEnable = (VkBool32) (bool) Settings.ColorBlendLogicOp,
|
||
.logicOp = Settings.ColorBlendLogicOp,
|
||
.attachmentCount = (uint32_t) Settings.ColorBlend.size(),
|
||
.pAttachments = Settings.ColorBlend.data(),
|
||
.blendConstants = { 0.0f, 0.0f, 0.0f, 0.0f }
|
||
};
|
||
|
||
std::copy(Settings.ColorBlendConstants, Settings.ColorBlendConstants + 4, cb.blendConstants);
|
||
|
||
// Вершины шейдера
|
||
VkPipelineVertexInputStateCreateInfo createInfoVertexInput =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.vertexBindingDescriptionCount = (uint32_t) Settings.ShaderVertexBindings.size(),
|
||
.pVertexBindingDescriptions = Settings.ShaderVertexBindings.data(),
|
||
.vertexAttributeDescriptionCount = (uint32_t) Settings.ShaderVertexAttribute.size(),
|
||
.pVertexAttributeDescriptions = Settings.ShaderVertexAttribute.data()
|
||
};
|
||
|
||
for(auto &obj : Settings.ShaderStages)
|
||
vkAssert(obj.module && "Шейдер не назначен");
|
||
|
||
VkGraphicsPipelineCreateInfo pipeline =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.stageCount = (uint32_t) Settings.ShaderStages.size(),
|
||
.pStages = Settings.ShaderStages.data(),
|
||
.pVertexInputState = &createInfoVertexInput,
|
||
.pInputAssemblyState = &ia,
|
||
.pTessellationState = nullptr,
|
||
.pViewportState = &vp,
|
||
.pRasterizationState = &Settings.Rasterization,
|
||
.pMultisampleState = &Settings.Multisample,
|
||
.pDepthStencilState = &Settings.DepthStencil,
|
||
.pColorBlendState = &cb,
|
||
.pDynamicState = &dynamicState,
|
||
.layout = *Settings.ShaderLayoutBindings,
|
||
.renderPass = instance->Graphics.RenderPass,
|
||
.subpass = Settings.Subpass,
|
||
.basePipelineHandle = nullptr,
|
||
.basePipelineIndex = 0
|
||
};
|
||
|
||
VkPipelineCacheCreateInfo infoPipelineCache;
|
||
memset(&infoPipelineCache, 0, sizeof(infoPipelineCache));
|
||
infoPipelineCache.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
|
||
|
||
vkAssert(!vkCreateGraphicsPipelines(instance->Graphics.Device, VK_NULL_HANDLE, 1, &pipeline, nullptr, &PipelineObj));
|
||
}
|
||
|
||
|
||
// Shader
|
||
|
||
|
||
ShaderModule::ShaderModule(std::string_view view)
|
||
: Source(view)
|
||
{}
|
||
|
||
void ShaderModule::free(Vulkan *instance)
|
||
{
|
||
if(Module && instance->Graphics.Device)
|
||
vkDestroyShaderModule(instance->Graphics.Device, Module, nullptr);
|
||
|
||
Module = nullptr;
|
||
}
|
||
|
||
void ShaderModule::init(Vulkan *instance)
|
||
{
|
||
if(!Module)
|
||
{
|
||
VkShaderModuleCreateInfo moduleCreateInfo = {
|
||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.codeSize = Source.size(),
|
||
.pCode = (const uint32_t*) Source.data()
|
||
};
|
||
|
||
if(vkCreateShaderModule(instance->Graphics.Device, &moduleCreateInfo, nullptr, &Module))
|
||
MAKE_ERROR("VkHandler: Ошибка загрузки шейдера для устройства " << (void*) instance->Graphics.Device);
|
||
}
|
||
}
|
||
|
||
ShaderModule::~ShaderModule() = default;
|
||
|
||
|
||
Buffer::Buffer(Vulkan *instance, VkDeviceSize bufferSize, VkBufferUsageFlags usage, VkMemoryPropertyFlags flags)
|
||
: Instance(instance)
|
||
{
|
||
vkAssert(instance);
|
||
vkAssert(instance->Graphics.Device);
|
||
|
||
const VkBufferCreateInfo buf_info =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.size = std::max<uint32_t>(bufferSize, 1),
|
||
.usage = usage,
|
||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||
.queueFamilyIndexCount = 0,
|
||
.pQueueFamilyIndices = nullptr
|
||
};
|
||
|
||
Size = bufferSize;
|
||
|
||
VkResult res = vkCreateBuffer(instance->Graphics.Device, &buf_info, nullptr, &Buff);
|
||
if(res)
|
||
MAKE_ERROR("Vulkan: ошибка создания буфера");
|
||
|
||
VkMemoryRequirements memReqs;
|
||
vkGetBufferMemoryRequirements(instance->Graphics.Device, Buff, &memReqs);
|
||
|
||
VkMemoryAllocateInfo memAlloc =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||
.pNext = nullptr,
|
||
.allocationSize = memReqs.size,
|
||
.memoryTypeIndex = 0
|
||
};
|
||
|
||
memAlloc.memoryTypeIndex = instance->memoryTypeFromProperties(memReqs.memoryTypeBits, flags);
|
||
|
||
vkAllocateMemory(instance->Graphics.Device, &memAlloc, nullptr, &Memory);
|
||
if(res)
|
||
MAKE_ERROR("VkHandler: ошибка выделения памяти на устройстве");
|
||
|
||
vkBindBufferMemory(instance->Graphics.Device, Buff, Memory, 0);
|
||
}
|
||
|
||
Buffer::~Buffer()
|
||
{
|
||
if(Instance && Instance->Graphics.Device)
|
||
{
|
||
Instance->beforeDraw([buff = Buff, memory = Memory](Vulkan *instance)
|
||
{
|
||
if(buff)
|
||
vkDestroyBuffer(instance->Graphics.Device, buff, nullptr);
|
||
if(memory)
|
||
vkFreeMemory(instance->Graphics.Device, memory, nullptr);
|
||
});
|
||
}
|
||
}
|
||
|
||
Buffer::Buffer(Buffer &&obj)
|
||
{
|
||
Instance = obj.Instance;
|
||
Buff = obj.Buff;
|
||
Memory = obj.Memory;
|
||
Size = obj.Size;
|
||
|
||
obj.Instance = nullptr;
|
||
}
|
||
|
||
Buffer& Buffer::operator=(Buffer &&obj)
|
||
{
|
||
std::swap(Instance, obj.Instance);
|
||
std::swap(Buff, obj.Buff);
|
||
std::swap(Memory, obj.Memory);
|
||
std::swap(Size, obj.Size);
|
||
return *this;
|
||
}
|
||
|
||
uint8_t* Buffer::mapMemory(VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags) const
|
||
{
|
||
void *data;
|
||
if(vkMapMemory(Instance->Graphics.Device, Memory, offset, size, flags, &data))
|
||
MAKE_ERROR("Ошибка выделения памяти устройства на хосте;");
|
||
|
||
return (uint8_t*) data;
|
||
}
|
||
|
||
void Buffer::unMapMemory() const
|
||
{
|
||
vkUnmapMemory(Instance->Graphics.Device, Memory);
|
||
}
|
||
|
||
|
||
CommandBuffer::CommandBuffer(Vulkan *instance)
|
||
: Instance(instance)
|
||
{
|
||
vkAssert(instance);
|
||
vkAssert(instance->Graphics.Device);
|
||
|
||
if(!instance->isRenderThread())
|
||
{
|
||
// Используем собственный пулл
|
||
const VkCommandPoolCreateInfo infoCmdPool =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||
.queueFamilyIndex = instance->getSettings().QueueGraphics
|
||
};
|
||
|
||
vkAssert(!vkCreateCommandPool(instance->Graphics.Device, &infoCmdPool, nullptr, &OffthreadPool));
|
||
}
|
||
|
||
vkAssert(instance->Graphics.Pool);
|
||
const VkCommandBufferAllocateInfo infoCmd =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||
.pNext = nullptr,
|
||
.commandPool = OffthreadPool ? OffthreadPool : instance->Graphics.Pool,
|
||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||
.commandBufferCount = 1
|
||
};
|
||
|
||
vkAssert(!vkAllocateCommandBuffers(instance->Graphics.Device, &infoCmd, &Buffer));
|
||
|
||
VkCommandBufferBeginInfo infoCmdBuffer =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.pInheritanceInfo = nullptr
|
||
};
|
||
|
||
vkAssert(!vkBeginCommandBuffer(Buffer, &infoCmdBuffer));
|
||
}
|
||
|
||
CommandBuffer::~CommandBuffer()
|
||
{
|
||
if(Buffer && Instance && Instance->Graphics.Device)
|
||
{
|
||
if(Instance->Graphics.DeviceQueueGraphic)
|
||
{
|
||
//vkAssert(!vkEndCommandBuffer(Buffer));
|
||
vkEndCommandBuffer(Buffer);
|
||
const VkCommandBuffer cmd_bufs[] = { Buffer };
|
||
VkFence nullFence = { VK_NULL_HANDLE };
|
||
VkSubmitInfo submit_info =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||
.pNext = nullptr,
|
||
.waitSemaphoreCount = 0,
|
||
.pWaitSemaphores = nullptr,
|
||
.pWaitDstStageMask = nullptr,
|
||
.commandBufferCount = 1,
|
||
.pCommandBuffers = cmd_bufs,
|
||
.signalSemaphoreCount = 0,
|
||
.pSignalSemaphores = nullptr
|
||
};
|
||
|
||
//vkAssert(!vkQueueSubmit(Instance->Graphics.DeviceQueueGraphic, 1, &submit_info, nullFence));
|
||
vkQueueSubmit(Instance->Graphics.DeviceQueueGraphic, 1, &submit_info, nullFence);
|
||
//vkAssert(!vkQueueWaitIdle(Instance->Graphics.DeviceQueueGraphic));
|
||
vkQueueWaitIdle(Instance->Graphics.DeviceQueueGraphic);
|
||
|
||
auto toExecute = std::move(AfterExecute);
|
||
for(auto &iter : toExecute)
|
||
try { iter(); } catch(const std::exception &exc) { Logger("CommandBuffer").error() << exc.what(); }
|
||
}
|
||
|
||
vkFreeCommandBuffers(Instance->Graphics.Device, OffthreadPool ? OffthreadPool : Instance->Graphics.Pool, 1, &Buffer);
|
||
|
||
if(OffthreadPool)
|
||
vkDestroyCommandPool(Instance->Graphics.Device, OffthreadPool, nullptr);
|
||
}
|
||
}
|
||
|
||
void CommandBuffer::execute()
|
||
{
|
||
vkAssert(Instance->Graphics.DeviceQueueGraphic);
|
||
vkAssert(!vkEndCommandBuffer(Buffer));
|
||
|
||
const VkCommandBuffer cmd_bufs[] = { Buffer };
|
||
VkFence nullFence = { VK_NULL_HANDLE };
|
||
VkSubmitInfo submit_info =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||
.pNext = nullptr,
|
||
.waitSemaphoreCount = 0,
|
||
.pWaitSemaphores = nullptr,
|
||
.pWaitDstStageMask = nullptr,
|
||
.commandBufferCount = 1,
|
||
.pCommandBuffers = cmd_bufs,
|
||
.signalSemaphoreCount = 0,
|
||
.pSignalSemaphores = nullptr
|
||
};
|
||
|
||
vkAssert(!vkQueueSubmit(Instance->Graphics.DeviceQueueGraphic, 1, &submit_info, nullFence));
|
||
vkAssert(!vkQueueWaitIdle(Instance->Graphics.DeviceQueueGraphic));
|
||
VkCommandBufferBeginInfo infoCmdBuffer =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.pInheritanceInfo = nullptr
|
||
};
|
||
|
||
vkAssert(!vkBeginCommandBuffer(Buffer, &infoCmdBuffer));
|
||
|
||
auto toExecute = std::move(AfterExecute);
|
||
for(auto &iter : toExecute)
|
||
iter();
|
||
}
|
||
|
||
void CommandBuffer::executeNoAwait()
|
||
{
|
||
execute();
|
||
}
|
||
|
||
//VkSimpleImage
|
||
void SimpleImage::postInit(const ByteBuffer &pixels, size_t width, size_t height)
|
||
{
|
||
CommandBuffer buffer(Instance);
|
||
|
||
Width = width;
|
||
Height = height;
|
||
|
||
constexpr VkFormat tex_format = VK_FORMAT_B8G8R8A8_UNORM;
|
||
ImageLayout = VK_IMAGE_LAYOUT_GENERAL; // То как будем использовать графический буфер, в данном случае как текстура
|
||
VkFormatProperties props;
|
||
|
||
vkGetPhysicalDeviceFormatProperties(Instance->Graphics.PhysicalDevice, tex_format, &props);
|
||
|
||
VkImageCreateInfo infoImageCreate =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.imageType = VK_IMAGE_TYPE_2D,
|
||
.format = tex_format,
|
||
.extent = { (uint32_t) width, (uint32_t) height, 1 },
|
||
.mipLevels = 1,
|
||
.arrayLayers = 1,
|
||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||
.tiling = VK_IMAGE_TILING_MAX_ENUM,
|
||
.usage = VK_IMAGE_USAGE_FLAG_BITS_MAX_ENUM,
|
||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||
.queueFamilyIndexCount = 0,
|
||
.pQueueFamilyIndices = 0,
|
||
.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED
|
||
};
|
||
|
||
VkMemoryAllocateInfo memoryAlloc =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||
.pNext = nullptr,
|
||
.allocationSize = 0,
|
||
.memoryTypeIndex = 0
|
||
};
|
||
|
||
VkMemoryRequirements memoryReqs;
|
||
const VkImageSubresource memorySubres = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, .arrayLayer = 0, };
|
||
|
||
VkImageMemoryBarrier infoImageMemoryBarrier =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||
.pNext = nullptr,
|
||
.srcAccessMask = VK_ACCESS_NONE,
|
||
.dstAccessMask = VK_ACCESS_NONE,
|
||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||
.newLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||
.srcQueueFamilyIndex = 0,
|
||
.dstQueueFamilyIndex = 0,
|
||
.image = VK_NULL_HANDLE,
|
||
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
|
||
};
|
||
|
||
/*
|
||
Сначала загрузим текстуру на стороне хоста
|
||
Создаём временную картинку
|
||
*/
|
||
|
||
VkImage tempImage;
|
||
VkDeviceMemory tempMemory;
|
||
|
||
infoImageCreate.tiling = VK_IMAGE_TILING_LINEAR;
|
||
infoImageCreate.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||
infoImageCreate.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
||
vkAssert(!vkCreateImage(Instance->Graphics.Device, &infoImageCreate, nullptr, &tempImage));
|
||
vkGetImageMemoryRequirements(Instance->Graphics.Device, tempImage, &memoryReqs);
|
||
|
||
memoryAlloc.allocationSize = memoryReqs.size;
|
||
memoryAlloc.memoryTypeIndex = Instance->memoryTypeFromProperties(memoryReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||
|
||
vkAssert(!vkAllocateMemory(Instance->Graphics.Device, &memoryAlloc, nullptr, &tempMemory));
|
||
vkAssert(!vkBindImageMemory(Instance->Graphics.Device, tempImage, tempMemory, 0));
|
||
|
||
// Заполняем данными
|
||
VkSubresourceLayout layout;
|
||
vkGetImageSubresourceLayout(Instance->Graphics.Device, tempImage, &memorySubres, &layout);
|
||
|
||
void *data;
|
||
vkAssert(!vkMapMemory(Instance->Graphics.Device, tempMemory, 0, memoryAlloc.allocationSize, 0, &data));
|
||
|
||
for (size_t y = 0; y < Height; y++) {
|
||
uint32_t *row = (uint32_t*) (((uint8_t*) data) + layout.rowPitch * y);
|
||
uint32_t *color = ((uint32_t*) pixels.data()) + Width*y;
|
||
for (size_t x = 0; x < Width; x++, row++, color++)
|
||
*row = *color;
|
||
}
|
||
|
||
vkUnmapMemory(Instance->Graphics.Device, tempMemory);
|
||
|
||
// Создаём барьер, чтобы следующие команды выполнялись только после того
|
||
// как будут завершены операции по переносу данных с хоста к драйверу
|
||
// И меняем layout на нужный
|
||
infoImageMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
|
||
infoImageMemoryBarrier.dstAccessMask = VK_ACCESS_NONE;
|
||
infoImageMemoryBarrier.oldLayout = infoImageCreate.initialLayout;
|
||
infoImageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; // Будет использоваться как источник данных
|
||
infoImageMemoryBarrier.image = tempImage;
|
||
|
||
vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||
0, 0, nullptr, 0, nullptr, 1, &infoImageMemoryBarrier);
|
||
|
||
// Формат конечной картинки
|
||
if (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)
|
||
infoImageCreate.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||
else if (props.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)
|
||
infoImageCreate.tiling = VK_IMAGE_TILING_LINEAR;
|
||
else
|
||
/* Can't support VK_FORMAT_B8G8R8A8_UNORM */
|
||
vkAssert(!"No support for B8G8R8A8_UNORM as texture image format");
|
||
|
||
/* Создаём конечную картинку */
|
||
infoImageCreate.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||
|
||
vkAssert(!vkCreateImage(Instance->Graphics.Device, &infoImageCreate, nullptr, &Image));
|
||
vkGetImageMemoryRequirements(Instance->Graphics.Device, Image, &memoryReqs);
|
||
|
||
memoryAlloc.allocationSize = memoryReqs.size;
|
||
memoryAlloc.memoryTypeIndex = Instance->memoryTypeFromProperties(memoryReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||
|
||
vkAssert(!vkAllocateMemory(Instance->Graphics.Device, &memoryAlloc, nullptr, &Memory));
|
||
vkAssert(!vkBindImageMemory(Instance->Graphics.Device, Image, Memory, 0));
|
||
|
||
// Задаём нужный layout
|
||
infoImageMemoryBarrier.srcAccessMask = VK_ACCESS_NONE;
|
||
infoImageMemoryBarrier.dstAccessMask = VK_ACCESS_NONE;
|
||
infoImageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||
infoImageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||
infoImageMemoryBarrier.image = Image;
|
||
|
||
vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||
0, 0, nullptr, 0, nullptr, 1, &infoImageMemoryBarrier);
|
||
|
||
VkImageCopy copy_region =
|
||
{
|
||
.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 },
|
||
.srcOffset = { 0, 0, 0 },
|
||
.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 },
|
||
.dstOffset = { 0, 0, 0 },
|
||
.extent = { uint32_t(Width), uint32_t(Height), 1 },
|
||
};
|
||
|
||
vkCmdCopyImage(buffer, tempImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||
Image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region);
|
||
|
||
infoImageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||
infoImageMemoryBarrier.newLayout = ImageLayout; // Используем как текстуру
|
||
vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||
0, 0, nullptr, 0, nullptr, 1, &infoImageMemoryBarrier);
|
||
|
||
// Выполняем все команды
|
||
buffer.execute();
|
||
|
||
// И удаляем не нужную картинку
|
||
vkDestroyImage(Instance->Graphics.Device, tempImage, nullptr);
|
||
vkFreeMemory(Instance->Graphics.Device, tempMemory, nullptr);
|
||
|
||
{
|
||
// Способ чтения картинки
|
||
const VkSamplerCreateInfo ciSampler =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.magFilter = VK_FILTER_NEAREST,
|
||
.minFilter = VK_FILTER_NEAREST,
|
||
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
|
||
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||
.mipLodBias = 0.0f,
|
||
.anisotropyEnable = VK_FALSE,
|
||
.maxAnisotropy = 1,
|
||
.compareEnable = 0,
|
||
.compareOp = VK_COMPARE_OP_NEVER,
|
||
.minLod = 0.0f,
|
||
.maxLod = 0.0f,
|
||
.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,
|
||
.unnormalizedCoordinates = VK_FALSE
|
||
};
|
||
|
||
vkAssert(!vkCreateSampler(Instance->Graphics.Device, &ciSampler, nullptr, &Sampler));
|
||
}
|
||
|
||
{
|
||
// Порядок пикселей и привязка к картинке
|
||
VkImageViewCreateInfo ciView =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.image = Image,
|
||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||
.format = tex_format,
|
||
.components =
|
||
{
|
||
VK_COMPONENT_SWIZZLE_B,
|
||
VK_COMPONENT_SWIZZLE_G,
|
||
VK_COMPONENT_SWIZZLE_R,
|
||
VK_COMPONENT_SWIZZLE_A
|
||
},
|
||
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
|
||
};
|
||
|
||
vkAssert(!vkCreateImageView(Instance->Graphics.Device, &ciView, nullptr, &View));
|
||
}
|
||
}
|
||
|
||
SimpleImage::SimpleImage(Vulkan *instance, std::filesystem::path filePNG)
|
||
{
|
||
Instance = instance;
|
||
size_t width, height;
|
||
bool alpha;
|
||
ByteBuffer pixels = loadTexture(filePNG, width, height, alpha);
|
||
postInit(std::move(pixels), width, height);
|
||
}
|
||
|
||
ByteBuffer SimpleImage::loadTexture(std::filesystem::path file, size_t &width, size_t &height, bool &alpha)
|
||
{
|
||
int _width, _height;
|
||
ByteBuffer buff = loadPNG(std::ifstream(file), _width, _height, alpha, false);
|
||
width = _width;
|
||
height = _height;
|
||
|
||
return buff;
|
||
}
|
||
|
||
SimpleImage::SimpleImage(Vulkan *instance, const ByteBuffer &pixels, size_t width, size_t height)
|
||
{
|
||
Instance = instance;
|
||
postInit(pixels, width, height);
|
||
}
|
||
|
||
SimpleImage::~SimpleImage()
|
||
{
|
||
if(Instance && Instance->Graphics.Device)
|
||
{
|
||
if(Image)
|
||
vkDestroyImage(Instance->Graphics.Device, Image, nullptr);
|
||
if(Memory)
|
||
vkFreeMemory(Instance->Graphics.Device, Memory, nullptr);
|
||
if(Sampler)
|
||
vkDestroySampler(Instance->Graphics.Device, Sampler, nullptr);
|
||
if(View)
|
||
vkDestroyImageView(Instance->Graphics.Device, View, nullptr);
|
||
}
|
||
}
|
||
|
||
DynamicImage::DynamicImage(Vulkan *instance, uint32_t width, uint32_t height, const uint32_t *rgba)
|
||
: Instance(instance)
|
||
{
|
||
vkAssert(instance);
|
||
|
||
ImageLayout = VK_IMAGE_LAYOUT_GENERAL; // То как будем использовать графический буфер, в данном случае как текстура
|
||
|
||
|
||
// Способ чтения картинки
|
||
const VkSamplerCreateInfo ciSampler =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.magFilter = VK_FILTER_NEAREST,
|
||
.minFilter = VK_FILTER_NEAREST,
|
||
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
|
||
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||
.mipLodBias = 0.0f,
|
||
.anisotropyEnable = VK_FALSE,
|
||
.maxAnisotropy = 1,
|
||
.compareEnable = 0,
|
||
.compareOp = VK_COMPARE_OP_NEVER,
|
||
.minLod = 0.0f,
|
||
.maxLod = 0.0f,
|
||
.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,
|
||
.unnormalizedCoordinates = VK_FALSE
|
||
};
|
||
|
||
changeSampler(&ciSampler);
|
||
//TOS_vkAssert(!vkCreateSampler(Instance->Graphics.Device, sampler, nullptr, &Sampler), "vkCreateSampler");
|
||
|
||
// При создании картинки вне потока графики, нужно дождаться, когда картинка будет создана
|
||
if(!Instance->isRenderThread() && Instance->isAlive())
|
||
{
|
||
bool flag = false;
|
||
std::exception_ptr exc;
|
||
size_t start = Time::getSeconds();
|
||
|
||
Instance->beforeDraw([obj = this, rgba, width, height, &exc, &flag, start](Vulkan *instance) {
|
||
try {
|
||
obj->recreateImage(width, height, rgba);
|
||
} catch(...) {
|
||
exc = std::current_exception();
|
||
}
|
||
|
||
if(Time::getSeconds() - start < 30)
|
||
flag = true;
|
||
});
|
||
|
||
for(size_t timer = 0; timer < 31500000 && !flag; timer++)
|
||
Time::sleep6(1);
|
||
|
||
if(!flag)
|
||
MAKE_ERROR("WatchDog: Превышено время ожидания (30 секунд) потока графики Vulkan");
|
||
|
||
if(exc)
|
||
std::rethrow_exception(exc);
|
||
} else
|
||
recreateImage(width, height, rgba);
|
||
|
||
|
||
IsFirstCreate = false;
|
||
}
|
||
|
||
DynamicImage::~DynamicImage()
|
||
{
|
||
if(Instance && Instance->Graphics.Device)
|
||
{
|
||
Instance->beforeDraw([image = Image, memory = Memory, sampler = Sampler, view = View](Vulkan *instance){
|
||
if(image)
|
||
vkDestroyImage(instance->Graphics.Device, image, nullptr);
|
||
if(memory)
|
||
vkFreeMemory(instance->Graphics.Device, memory, nullptr);
|
||
if(sampler)
|
||
vkDestroySampler(instance->Graphics.Device, sampler, nullptr);
|
||
if(view)
|
||
vkDestroyImageView(instance->Graphics.Device, view, nullptr);
|
||
});
|
||
|
||
|
||
// if(Image)
|
||
// vkDestroyImage(Instance->Graphics.Device, Image, nullptr);
|
||
// if(Memory)
|
||
// vkFreeMemory(Instance->Graphics.Device, Memory, nullptr);
|
||
// if(Sampler)
|
||
// vkDestroySampler(Instance->Graphics.Device, Sampler, nullptr);
|
||
// if(View)
|
||
// vkDestroyImageView(Instance->Graphics.Device, View, nullptr);
|
||
}
|
||
}
|
||
|
||
void DynamicImage::changeSampler(const VkSamplerCreateInfo *sampler)
|
||
{
|
||
if(!IsFirstCreate && (Instance->Screen.State == Vulkan::DrawState::Drawing || !Instance->isRenderThread()) && Instance->isAlive())
|
||
{
|
||
// Нельзя пересоздать картинку сейчас
|
||
std::shared_ptr<DynamicImage> obj = shared_from_this();
|
||
|
||
if(!IsFirstCreate)
|
||
vkAssert(obj && "Чтобы изменять картинку во время рендера сцены, она должна быть Shared");
|
||
|
||
Instance->beforeDraw([obj, info = *sampler](Vulkan *instance)
|
||
{
|
||
obj->changeSampler(&info);
|
||
});
|
||
|
||
return;
|
||
}
|
||
|
||
auto oldSampler = Sampler;
|
||
vkAssert(!vkCreateSampler(Instance->Graphics.Device, sampler, nullptr, &Sampler));
|
||
|
||
// Обновляем дескрипторы
|
||
for(size_t iter = 0; iter < AfterRecreate.size(); iter++)
|
||
{
|
||
if(!AfterRecreate[iter]())
|
||
{
|
||
AfterRecreate.erase(AfterRecreate.begin() + iter);
|
||
iter--;
|
||
}
|
||
}
|
||
|
||
if(oldSampler)
|
||
vkDestroySampler(Instance->Graphics.Device, oldSampler, nullptr);
|
||
}
|
||
|
||
void DynamicImage::recreateImage(uint16_t width, uint16_t height, const uint32_t *rgba)
|
||
{
|
||
if(!IsFirstCreate && (Instance->Screen.State == Vulkan::DrawState::Drawing || !Instance->isRenderThread()) && Instance->isAlive())
|
||
{
|
||
// Нельзя пересоздать картинку сейчас
|
||
std::shared_ptr<DynamicImage> obj = shared_from_this();
|
||
|
||
if(!IsFirstCreate)
|
||
vkAssert(obj && "Чтобы изменять картинку во время рендера сцены, она должна быть Shared");
|
||
|
||
ByteBuffer buff(size_t(width)*size_t(height)*4);
|
||
if(rgba)
|
||
std::copy(rgba, rgba+buff.size()/4, (uint32_t*) buff.data());
|
||
|
||
Width = width;
|
||
Height = height;
|
||
|
||
Instance->beforeDraw([obj, buff, width, height](Vulkan *instance) {
|
||
obj->recreateImage(width, height, (uint32_t*) buff.data());
|
||
});
|
||
|
||
return;
|
||
}
|
||
|
||
Width = width;
|
||
Height = height;
|
||
|
||
if(width == 0)
|
||
width = 1;
|
||
|
||
if(height == 0)
|
||
height = 1;
|
||
|
||
// Нужно удалить их после того как будут обновлены дексрипторы
|
||
auto oldImage = Image;
|
||
Image = nullptr;
|
||
auto oldMemory = Memory;
|
||
Memory = nullptr;
|
||
auto oldView = View;
|
||
View = nullptr;
|
||
|
||
// Создаём конечную картинку
|
||
VkImageCreateInfo infoImageCreate =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.imageType = VK_IMAGE_TYPE_2D,
|
||
.format = VK_FORMAT_B8G8R8A8_UNORM,
|
||
.extent = { (uint32_t) width, (uint32_t) height, 1 },
|
||
.mipLevels = 1,
|
||
.arrayLayers = 1,
|
||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||
.tiling = VK_IMAGE_TILING_MAX_ENUM,
|
||
.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||
.queueFamilyIndexCount = 0,
|
||
.pQueueFamilyIndices = 0,
|
||
.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED
|
||
};
|
||
|
||
VkFormatProperties props;
|
||
vkGetPhysicalDeviceFormatProperties(Instance->Graphics.PhysicalDevice, infoImageCreate.format, &props);
|
||
|
||
if (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)
|
||
infoImageCreate.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||
else if (props.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)
|
||
infoImageCreate.tiling = VK_IMAGE_TILING_LINEAR;
|
||
else
|
||
/* Can't support VK_FORMAT_B8G8R8A8_UNORM */
|
||
vkAssert(!"No support for B8G8R8A8_UNORM as texture image format");
|
||
|
||
vkAssert(!vkCreateImage(Instance->Graphics.Device, &infoImageCreate, nullptr, &Image));
|
||
|
||
// Выделяем память
|
||
VkMemoryRequirements memoryReqs;
|
||
const VkImageSubresource memorySubres = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, .arrayLayer = 0, };
|
||
|
||
vkGetImageMemoryRequirements(Instance->Graphics.Device, Image, &memoryReqs);
|
||
|
||
VkMemoryAllocateInfo memoryAlloc =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||
.pNext = nullptr,
|
||
.allocationSize = memoryReqs.size,
|
||
.memoryTypeIndex = Instance->memoryTypeFromProperties(memoryReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
|
||
};
|
||
|
||
vkAssert(!vkAllocateMemory(Instance->Graphics.Device, &memoryAlloc, nullptr, &Memory));
|
||
vkAssert(!vkBindImageMemory(Instance->Graphics.Device, Image, Memory, 0));
|
||
|
||
// Порядок пикселей и привязка к картинке
|
||
VkImageViewCreateInfo ciView =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.image = Image,
|
||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||
.format = infoImageCreate.format,
|
||
.components =
|
||
{
|
||
VK_COMPONENT_SWIZZLE_B,
|
||
VK_COMPONENT_SWIZZLE_G,
|
||
VK_COMPONENT_SWIZZLE_R,
|
||
VK_COMPONENT_SWIZZLE_A
|
||
},
|
||
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
|
||
};
|
||
|
||
vkAssert(!vkCreateImageView(Instance->Graphics.Device, &ciView, nullptr, &View));
|
||
|
||
// Обновляем дескрипторы
|
||
for(size_t iter = 0; iter < AfterRecreate.size(); iter++)
|
||
{
|
||
if(!AfterRecreate[iter]())
|
||
{
|
||
AfterRecreate.erase(AfterRecreate.begin() + iter);
|
||
iter--;
|
||
}
|
||
}
|
||
|
||
// Удаляем старые объекты
|
||
Instance->beforeDraw([oldImage, oldMemory, oldView](Vulkan *instance){
|
||
if(oldImage)
|
||
vkDestroyImage(instance->Graphics.Device, oldImage, nullptr);
|
||
if(oldMemory)
|
||
vkFreeMemory(instance->Graphics.Device, oldMemory, nullptr);
|
||
if(oldView)
|
||
vkDestroyImageView(instance->Graphics.Device, oldView, nullptr);
|
||
});
|
||
|
||
changeData(0, 0, Width, Height, rgba);
|
||
}
|
||
|
||
void DynamicImage::changeData(const uint32_t *rgba)
|
||
{
|
||
changeData(0, 0, Width, Height, rgba);
|
||
}
|
||
|
||
void DynamicImage::changeData(int32_t x, int32_t y, uint16_t width, uint16_t height, const uint32_t *rgba)
|
||
{
|
||
vkAssert(width <= Width && height <= Height && "Превышен размер обновляемых данных width <= Width && height <= Height");
|
||
|
||
if(IsFirstCreate)
|
||
{
|
||
VkImageMemoryBarrier infoImageMemoryBarrier =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||
.pNext = nullptr,
|
||
.srcAccessMask = VK_ACCESS_NONE,
|
||
.dstAccessMask = VK_ACCESS_NONE,
|
||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||
.newLayout = ImageLayout,
|
||
.srcQueueFamilyIndex = 0,
|
||
.dstQueueFamilyIndex = 0,
|
||
.image = Image,
|
||
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
|
||
};
|
||
|
||
CommandBuffer buffer(Instance);
|
||
vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||
0, 0, nullptr, 0, nullptr, 1, &infoImageMemoryBarrier);
|
||
buffer.execute();
|
||
return;
|
||
} else if(width == 0 || height == 0)
|
||
return;
|
||
|
||
if(Instance->isAlive() && (Instance->Screen.State == Vulkan::DrawState::Drawing || !Instance->isRenderThread()))
|
||
{
|
||
// Нельзя обновить картинку сейчас
|
||
std::shared_ptr<DynamicImage> obj = shared_from_this();
|
||
vkAssert(obj && "Чтобы изменять картинку во время рендера сцены, она должна быть Shared");
|
||
|
||
ByteBuffer buff(size_t(width)*size_t(height)*4, (const uint8_t*) rgba);
|
||
|
||
Instance->beforeDraw([obj, buff, x, y, width, height](Vulkan *instance) {
|
||
obj->changeData(x, y, width, height, (uint32_t*) buff.data());
|
||
});
|
||
|
||
return;
|
||
}
|
||
|
||
CommandBuffer buffer(Instance);
|
||
VkFormatProperties props;
|
||
vkGetPhysicalDeviceFormatProperties(Instance->Graphics.PhysicalDevice, VK_FORMAT_B8G8R8A8_UNORM, &props);
|
||
|
||
VkImageMemoryBarrier infoImageMemoryBarrier =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||
.pNext = nullptr,
|
||
.srcAccessMask = VK_ACCESS_NONE,
|
||
.dstAccessMask = VK_ACCESS_NONE,
|
||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||
.newLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||
.srcQueueFamilyIndex = 0,
|
||
.dstQueueFamilyIndex = 0,
|
||
.image = VK_NULL_HANDLE,
|
||
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
|
||
};
|
||
|
||
const VkImageSubresource memorySubres = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, .arrayLayer = 0, };
|
||
|
||
// Создаём временную картинку
|
||
VkImage tempImage;
|
||
VkDeviceMemory tempMemory;
|
||
|
||
VkImageCreateInfo infoImageCreate =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.imageType = VK_IMAGE_TYPE_2D,
|
||
.format = VK_FORMAT_B8G8R8A8_UNORM,
|
||
.extent = { (uint32_t) width, (uint32_t) height, 1 },
|
||
.mipLevels = 1,
|
||
.arrayLayers = 1,
|
||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||
.tiling = VK_IMAGE_TILING_LINEAR,
|
||
.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
|
||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||
.queueFamilyIndexCount = 0,
|
||
.pQueueFamilyIndices = 0,
|
||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED
|
||
};
|
||
|
||
VkMemoryRequirements memoryReqs;
|
||
vkAssert(!vkCreateImage(Instance->Graphics.Device, &infoImageCreate, nullptr, &tempImage));
|
||
vkGetImageMemoryRequirements(Instance->Graphics.Device, tempImage, &memoryReqs);
|
||
|
||
VkMemoryAllocateInfo memoryAlloc =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||
.pNext = nullptr,
|
||
.allocationSize = memoryReqs.size,
|
||
.memoryTypeIndex = Instance->memoryTypeFromProperties(memoryReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
|
||
};
|
||
|
||
vkAssert(!vkAllocateMemory(Instance->Graphics.Device, &memoryAlloc, nullptr, &tempMemory));
|
||
vkAssert(!vkBindImageMemory(Instance->Graphics.Device, tempImage, tempMemory, 0));
|
||
|
||
// Заполняем данными
|
||
VkSubresourceLayout layout;
|
||
vkGetImageSubresourceLayout(Instance->Graphics.Device, tempImage, &memorySubres, &layout);
|
||
|
||
void *data;
|
||
vkAssert(!vkMapMemory(Instance->Graphics.Device, tempMemory, 0, memoryAlloc.allocationSize, 0, &data));
|
||
|
||
if(rgba)
|
||
{
|
||
for (size_t y = 0; y < height; y++)
|
||
{
|
||
uint32_t *row = (uint32_t*) (((uint8_t*) data) + layout.rowPitch * y);
|
||
uint32_t *color = ((uint32_t*) rgba) + uint32_t(width)*y;
|
||
for (size_t x = 0; x < width; x++, row++, color++)
|
||
*row = *color;
|
||
}
|
||
} else {
|
||
uint32_t *start = (uint32_t*) data;
|
||
for(size_t begin = 0, end = memoryAlloc.allocationSize/4; begin != end; begin++, start++)
|
||
*start = 0;
|
||
}
|
||
|
||
vkUnmapMemory(Instance->Graphics.Device, tempMemory);
|
||
|
||
// Создаём барьер, чтобы следующие команды выполнялись только после того
|
||
// как будут завершены операции по переносу данных с хоста к драйверу
|
||
// И меняем layout на нужный
|
||
infoImageMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
|
||
infoImageMemoryBarrier.dstAccessMask = VK_ACCESS_NONE;
|
||
infoImageMemoryBarrier.oldLayout = infoImageCreate.initialLayout;
|
||
infoImageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; // Будет использоваться как источник данных
|
||
infoImageMemoryBarrier.image = tempImage;
|
||
|
||
vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||
0, 0, nullptr, 0, nullptr, 1, &infoImageMemoryBarrier);
|
||
|
||
// Задаём нужный layout
|
||
infoImageMemoryBarrier.srcAccessMask = VK_ACCESS_NONE;
|
||
infoImageMemoryBarrier.dstAccessMask = VK_ACCESS_NONE;
|
||
infoImageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||
infoImageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||
infoImageMemoryBarrier.image = Image;
|
||
|
||
vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||
0, 0, nullptr, 0, nullptr, 1, &infoImageMemoryBarrier);
|
||
|
||
VkImageCopy copy_region =
|
||
{
|
||
.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 },
|
||
.srcOffset = { 0, 0, 0 },
|
||
.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 },
|
||
.dstOffset = { x, y, 0 },
|
||
.extent = { width, height, 1 },
|
||
};
|
||
|
||
vkCmdCopyImage(buffer, tempImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||
Image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region);
|
||
|
||
infoImageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||
infoImageMemoryBarrier.newLayout = ImageLayout; // Используем как текстуру
|
||
vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||
0, 0, nullptr, 0, nullptr, 1, &infoImageMemoryBarrier);
|
||
|
||
// Выполняем все команды
|
||
buffer.execute();
|
||
|
||
// Удаляем не нужную картинку
|
||
vkDestroyImage(Instance->Graphics.Device, tempImage, nullptr);
|
||
vkFreeMemory(Instance->Graphics.Device, tempMemory, nullptr);
|
||
}
|
||
|
||
|
||
void DynamicImage::readData(int32_t x, int32_t y, uint16_t width, uint16_t height, uint32_t *rgba)
|
||
{
|
||
const VkImageSubresource memorySubres = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, .arrayLayer = 0, };
|
||
|
||
CommandBuffer buffer(Instance);
|
||
VkFormatProperties props;
|
||
vkGetPhysicalDeviceFormatProperties(Instance->Graphics.PhysicalDevice, VK_FORMAT_B8G8R8A8_UNORM, &props);
|
||
|
||
VkImageMemoryBarrier infoImageMemoryBarrier =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||
.pNext = nullptr,
|
||
.srcAccessMask = VK_ACCESS_NONE,
|
||
.dstAccessMask = VK_ACCESS_NONE,
|
||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||
.newLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||
.srcQueueFamilyIndex = 0,
|
||
.dstQueueFamilyIndex = 0,
|
||
.image = VK_NULL_HANDLE,
|
||
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
|
||
};
|
||
|
||
// Создаём временную картинку
|
||
VkImage tempImage;
|
||
VkDeviceMemory tempMemory;
|
||
|
||
VkImageCreateInfo infoImageCreate =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.imageType = VK_IMAGE_TYPE_2D,
|
||
.format = VK_FORMAT_B8G8R8A8_UNORM,
|
||
.extent = { (uint32_t) width, (uint32_t) height, 1 },
|
||
.mipLevels = 1,
|
||
.arrayLayers = 1,
|
||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||
.tiling = VK_IMAGE_TILING_LINEAR,
|
||
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||
.queueFamilyIndexCount = 0,
|
||
.pQueueFamilyIndices = 0,
|
||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED
|
||
};
|
||
|
||
VkMemoryRequirements memoryReqs;
|
||
vkAssert(!vkCreateImage(Instance->Graphics.Device, &infoImageCreate, nullptr, &tempImage));
|
||
vkGetImageMemoryRequirements(Instance->Graphics.Device, tempImage, &memoryReqs);
|
||
|
||
VkMemoryAllocateInfo memoryAlloc =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||
.pNext = nullptr,
|
||
.allocationSize = memoryReqs.size,
|
||
.memoryTypeIndex = Instance->memoryTypeFromProperties(memoryReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
|
||
};
|
||
|
||
vkAssert(!vkAllocateMemory(Instance->Graphics.Device, &memoryAlloc, nullptr, &tempMemory));
|
||
vkAssert(!vkBindImageMemory(Instance->Graphics.Device, tempImage, tempMemory, 0));
|
||
|
||
// Подготавливаем изображение к приёму данных
|
||
infoImageMemoryBarrier.srcAccessMask = VK_ACCESS_NONE;
|
||
infoImageMemoryBarrier.dstAccessMask = VK_ACCESS_NONE;
|
||
infoImageMemoryBarrier.oldLayout = infoImageCreate.initialLayout;
|
||
infoImageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||
infoImageMemoryBarrier.image = tempImage;
|
||
|
||
vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||
0, 0, nullptr, 0, nullptr, 1, &infoImageMemoryBarrier);
|
||
|
||
infoImageMemoryBarrier.oldLayout = ImageLayout;
|
||
infoImageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||
infoImageMemoryBarrier.image = Image;
|
||
|
||
vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||
0, 0, nullptr, 0, nullptr, 1, &infoImageMemoryBarrier);
|
||
|
||
// Копируем нужный участок
|
||
VkImageCopy copy_region =
|
||
{
|
||
.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 },
|
||
.srcOffset = { x, y, 0 },
|
||
.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 },
|
||
.dstOffset = { 0, 0, 0 },
|
||
.extent = { uint32_t(width), uint32_t(height), 1 },
|
||
};
|
||
|
||
vkCmdCopyImage(buffer, Image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||
tempImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region);
|
||
|
||
buffer.execute();
|
||
|
||
infoImageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||
infoImageMemoryBarrier.newLayout = ImageLayout;
|
||
vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||
0, 0, nullptr, 0, nullptr, 1, &infoImageMemoryBarrier);
|
||
|
||
buffer.execute();
|
||
|
||
// Забираем данные
|
||
VkSubresourceLayout layout;
|
||
vkGetImageSubresourceLayout(Instance->Graphics.Device, tempImage, &memorySubres, &layout);
|
||
|
||
void *data;
|
||
vkAssert(!vkMapMemory(Instance->Graphics.Device, tempMemory, 0, memoryAlloc.allocationSize, 0, &data));
|
||
|
||
for (size_t y = 0; y < height; y++)
|
||
{
|
||
uint32_t *row = (uint32_t*) (((uint8_t*) data) + layout.rowPitch * y);
|
||
uint32_t *color = ((uint32_t*) rgba) + uint32_t(width)*y;
|
||
for (size_t x = 0; x < width; x++, row++, color++)
|
||
*color = *row;
|
||
}
|
||
|
||
vkUnmapMemory(Instance->Graphics.Device, tempMemory);
|
||
|
||
// И удаляем не нужную картинку
|
||
vkDestroyImage(Instance->Graphics.Device, tempImage, nullptr);
|
||
vkFreeMemory(Instance->Graphics.Device, tempMemory, nullptr);
|
||
}
|
||
|
||
|
||
|
||
AtlasImage::AtlasImage(Vulkan *instance)
|
||
: DynamicImage(instance),
|
||
UniformSchema(instance, sizeof(UniformInfo),
|
||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT),
|
||
HostBuffer(instance, 1024, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT)
|
||
{
|
||
}
|
||
|
||
AtlasImage::~AtlasImage() {}
|
||
|
||
void AtlasImage::claimTexture(uint16_t id, uint16_t width, uint16_t height)
|
||
{
|
||
auto &info = SubTextures[id];
|
||
info.Width = width;
|
||
info.Height = height;
|
||
|
||
// Всё равно будет пересборка
|
||
if(NeedRebuild)
|
||
{
|
||
if(!CachedData.contains(id))
|
||
CachedData.insert({id, {}});
|
||
|
||
return;
|
||
}
|
||
|
||
uint16_t NowHeight = 0;
|
||
uint16_t nextHeight;
|
||
|
||
for(; NowHeight < Heights.size(); NowHeight++)
|
||
{
|
||
nextHeight = Heights.size();
|
||
|
||
for(uint32_t x = 0; x < Heights.size(); x++)
|
||
{
|
||
// Текстура уже не влазит по высоте
|
||
if(height > Heights.size()-NowHeight)
|
||
{
|
||
NeedRebuild = true;
|
||
CachedData[id] = {};
|
||
return;
|
||
}
|
||
|
||
// Найти свободную точку
|
||
for(; x < Heights.size() && Heights[x] > NowHeight; x++)
|
||
if(Heights[x] > NowHeight && Heights[x] < nextHeight)
|
||
nextHeight = Heights[x];
|
||
|
||
// Определить сколько места свободно
|
||
uint32_t offset;
|
||
for(offset = x; offset < Heights.size() && Heights[offset] <= NowHeight; offset++)
|
||
if(Heights[offset] > NowHeight && Heights[offset] < nextHeight)
|
||
nextHeight = Heights[offset];
|
||
|
||
if(offset-x >= width)
|
||
{
|
||
// Нашли место
|
||
info.PosX = x;
|
||
info.PosY = NowHeight;
|
||
|
||
for(uint32_t iter = 0; iter < width; iter++, x++)
|
||
Heights[x] = NowHeight+height;
|
||
|
||
NeedUpdateSchema = true;
|
||
|
||
return;
|
||
}
|
||
|
||
// Мало места, поищем ещё
|
||
x = offset-1;
|
||
}
|
||
|
||
// На этом уровне не нашли, взбираемся повыше
|
||
NowHeight = nextHeight-1;
|
||
}
|
||
|
||
|
||
// Нету места :(
|
||
NeedRebuild = true;
|
||
CachedData[id] = {};
|
||
}
|
||
|
||
void AtlasImage::optimizeFreeIds()
|
||
{
|
||
if(IdsChanges++ < 16)
|
||
return;
|
||
|
||
if(SubTextures.empty())
|
||
{
|
||
FreeIds.clear();
|
||
return;
|
||
}
|
||
|
||
// Последний занятый идентификатор
|
||
uint16_t maxId = SubTextures.rend()->first;
|
||
|
||
// Всё, что выше максимального идентификатора будет удалено
|
||
std::vector<uint16_t> idToDelete;
|
||
|
||
uint16_t *obj = FreeIds.data();
|
||
for(size_t iter = 0; iter != FreeIds.size(); iter++, obj++)
|
||
if(*obj > maxId)
|
||
idToDelete.push_back(iter);
|
||
|
||
std::vector<uint16_t> newFreeIds(FreeIds.size() - idToDelete.size());
|
||
obj = FreeIds.data();
|
||
uint16_t *newIds = newFreeIds.data();
|
||
auto toDelete = idToDelete.begin();
|
||
|
||
for(size_t iter = 0; iter != FreeIds.size(); iter++, obj++)
|
||
if(toDelete == idToDelete.end() || *obj != *toDelete)
|
||
*(newIds++) = *obj;
|
||
else
|
||
toDelete++;
|
||
|
||
FreeIds = std::move(newFreeIds);
|
||
}
|
||
|
||
uint16_t AtlasImage::atlasAddTexture(uint16_t width, uint16_t height)
|
||
{
|
||
std::lock_guard lock(Changes);
|
||
|
||
if(FreeIds.size())
|
||
{
|
||
uint16_t id = FreeIds.back();
|
||
FreeIds.pop_back();
|
||
|
||
claimTexture(id, width, height);
|
||
return id;
|
||
}
|
||
|
||
if(SubTextures.empty())
|
||
{
|
||
claimTexture(0, width, height);
|
||
return 0;
|
||
}
|
||
|
||
uint16_t id = SubTextures.rbegin()->first;
|
||
if(id == 65535)
|
||
MAKE_ERROR("Закончились свободные идентификаторы в атласе");
|
||
|
||
id++;
|
||
claimTexture(id, width, height);
|
||
return id;
|
||
}
|
||
|
||
void AtlasImage::atlasRemoveTexture(uint16_t id)
|
||
{
|
||
std::lock_guard lock(Changes);
|
||
|
||
auto iter = SubTextures.find(id);
|
||
if(iter == SubTextures.end())
|
||
MAKE_ERROR("Идентификатор не существует");
|
||
|
||
SubTextures.erase(iter);
|
||
FreeIds.push_back(id);
|
||
|
||
optimizeFreeIds();
|
||
}
|
||
|
||
void AtlasImage::atlasResizeTexture(uint16_t id, uint16_t width, uint16_t height)
|
||
{
|
||
std::lock_guard lock(Changes);
|
||
|
||
InfoSubTexture *info = const_cast<InfoSubTexture*>(atlasGetTextureInfo(id));
|
||
|
||
{
|
||
auto iter = CachedData.find(id);
|
||
if(iter != CachedData.end())
|
||
{
|
||
// Убираем данные, запланированные на запись в атлас, только если текстура уже находится в атласе
|
||
if(iter->second.size())
|
||
CachedData.erase(iter);
|
||
}
|
||
}
|
||
|
||
// Если атлас будет пересобран, то просто меняем размер
|
||
if(NeedRebuild)
|
||
{
|
||
info->Width = width;
|
||
info->Height = height;
|
||
} else /* Используем уже выделенное место */ if(width <= info->Width && height <= info->Height)
|
||
{
|
||
info->Width = width;
|
||
info->Height = height;
|
||
|
||
NeedUpdateSchema = true;
|
||
} else /* Выделим новое место */ {
|
||
claimTexture(id, width, height);
|
||
// Либо повезёт и найдётся место в текущем атласе, либо придётся его весь пересоздать
|
||
}
|
||
}
|
||
|
||
void AtlasImage::atlasChangeTextureData(uint16_t id, const uint32_t *rgba)
|
||
{
|
||
std::lock_guard lock(Changes);
|
||
|
||
InfoSubTexture *info = const_cast<InfoSubTexture*>(atlasGetTextureInfo(id));
|
||
|
||
auto iter = CachedData.find(id);
|
||
// Если есть данные в кэше, то меняем их
|
||
if(iter != CachedData.end())
|
||
{
|
||
if(iter->second.size() == 0)
|
||
iter->second.resize(size_t(info->Width)*size_t(info->Height)*4);
|
||
|
||
std::copy(rgba, rgba+iter->second.size()/4, (uint32_t*) iter->second.data());
|
||
} else {
|
||
// Или меняем напрямую в атласе
|
||
changeData(info->PosX, info->PosY, info->Width, info->Height, rgba);
|
||
}
|
||
}
|
||
|
||
void AtlasImage::atlasSetAnimation(uint16_t id, bool enabled, uint16_t frames, float timePerFrame)
|
||
{
|
||
std::lock_guard lock(Changes);
|
||
|
||
InfoSubTexture *info = const_cast<InfoSubTexture*>(atlasGetTextureInfo(id));
|
||
|
||
info->Animation.Enabled = enabled;
|
||
info->Animation.Frames = frames;
|
||
info->Animation.TimePerFrame = timePerFrame;
|
||
|
||
NeedUpdateSchema = true;
|
||
}
|
||
|
||
void AtlasImage::atlasClear()
|
||
{
|
||
std::lock_guard lock(Changes);
|
||
|
||
FreeIds.clear();
|
||
CachedData.clear();
|
||
Heights.clear();
|
||
SubTextures.clear();
|
||
|
||
recreateImage(0, 0, nullptr);
|
||
|
||
NeedUpdateSchema = true;
|
||
}
|
||
|
||
void AtlasImage::atlasAddCallbackOnUniformChange(std::function<bool()> &&callback)
|
||
{
|
||
AfterUniformChange.push_back(std::move(callback));
|
||
}
|
||
|
||
void AtlasImage::atlasUpdateDynamicData()
|
||
{
|
||
vkAssert(Instance->isRenderThread() && "Обновление должно вызываться в потоке рендера");
|
||
|
||
std::lock_guard lock(Changes);
|
||
|
||
// Если необходимо, то пересобрать атлас
|
||
if(NeedRebuild)
|
||
{
|
||
NeedRebuild = false;
|
||
|
||
// 16×2^10 предел
|
||
size_t edge = 16;
|
||
|
||
// Сортируем идентификаторы по ширине текстуры
|
||
// И вычислим площадь всех текстур, чтобы лишний раз не пробывать размер
|
||
|
||
size_t square = 0;
|
||
uint16_t maxEdge = 0;
|
||
|
||
std::vector<uint16_t> ids(SubTextures.size());
|
||
{
|
||
uint16_t *id = ids.data();
|
||
for(auto &iter : SubTextures)
|
||
{
|
||
*(id++) = iter.first;
|
||
square += size_t(iter.second.Width)*size_t(iter.second.Height);
|
||
maxEdge = std::max(maxEdge, iter.second.Width);
|
||
maxEdge = std::max(maxEdge, iter.second.Height);
|
||
}
|
||
}
|
||
|
||
std::sort(ids.begin(), ids.end(), [&](const uint16_t left, const uint16_t right) { return int(SubTextures[left].Width) > int(SubTextures[right].Width); });
|
||
|
||
uint8_t pow = std::ceil(std::log2(std::sqrt(float(square))));
|
||
if(pow > 32)
|
||
MAKE_ERROR("В атласе недостаточно места, требуется объём 2^" << pow);
|
||
|
||
pow = std::max<uint8_t>(pow, std::ceil(std::log2(float(maxEdge))));
|
||
|
||
if(pow > 4)
|
||
edge <<= (pow-4);
|
||
|
||
// Нужно извлечь текстуры из атласа
|
||
for(auto &iter : SubTextures)
|
||
{
|
||
if(CachedData.contains(iter.first))
|
||
continue;
|
||
|
||
ByteBuffer &cache = CachedData[iter.first];
|
||
cache.resize(size_t(iter.second.Width)*size_t(iter.second.Height)*4);
|
||
readData(iter.second.PosX, iter.second.PosY, iter.second.Width, iter.second.Height, (uint32_t*) cache.data());
|
||
}
|
||
|
||
bool end = false;
|
||
for(uint8_t powder = std::max<uint8_t>(pow, 4); powder < 14; powder++, edge *= 2)
|
||
{
|
||
NeedRebuild = false;
|
||
|
||
// Подбираем подходящий размер атласа
|
||
Heights.resize(edge);
|
||
std::fill((uint64_t*) Heights.data(), ((uint64_t*) Heights.data())+edge/4, 0);
|
||
|
||
// Теперь размещаем текстуры
|
||
for(uint16_t id : ids)
|
||
{
|
||
auto &info = SubTextures[id];
|
||
|
||
ByteBuffer temp = std::move(CachedData[id]);
|
||
claimTexture(id, info.Width, info.Height);
|
||
CachedData[id] = std::move(temp);
|
||
|
||
if(NeedRebuild)
|
||
{
|
||
// Текстура не влезла
|
||
break;
|
||
}
|
||
}
|
||
|
||
if(NeedRebuild)
|
||
continue;
|
||
|
||
break;
|
||
}
|
||
|
||
if(NeedRebuild)
|
||
{
|
||
NeedRebuild = false;
|
||
MAKE_ERROR("Текстуры не уместились в атласе");
|
||
}
|
||
|
||
// Заполним атлас из кеша
|
||
recreateImage(uint16_t(edge), uint16_t(edge), nullptr);
|
||
|
||
for(auto &iter : SubTextures)
|
||
{
|
||
ByteBuffer &buff = CachedData[iter.first];
|
||
|
||
if(buff.empty())
|
||
continue;
|
||
|
||
changeData(iter.second.PosX, iter.second.PosY, iter.second.Width, iter.second.Height, (const uint32_t*) buff.data());
|
||
}
|
||
|
||
CachedData.clear();
|
||
NeedUpdateSchema = true;
|
||
|
||
// Обновим uniform буфер
|
||
}
|
||
|
||
|
||
// Если необходимо, обновить схему в uniform буфере
|
||
if(NeedUpdateSchema)
|
||
{
|
||
NeedUpdateSchema = false;
|
||
uint16_t count = SubTextures.empty() ? 0 : SubTextures.rbegin()->first+1;
|
||
|
||
// Удалим буфер после обновления дескрипторов
|
||
auto oldBuffer = VK::Buffer(Instance,
|
||
sizeof(UniformInfo) + size_t(count)*sizeof(InfoSubTexture),
|
||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||
std::swap(oldBuffer, UniformSchema);
|
||
|
||
UniformInfo *data = (UniformInfo*) HostBuffer.mapMemory();
|
||
data->SubsCount = count;
|
||
data->Counter = uint32_t(glfwGetTime()*256);
|
||
data->Width = Width;
|
||
data->Height = Height;
|
||
|
||
VkBufferCopy region = {
|
||
.srcOffset = 0,
|
||
.dstOffset = 0,
|
||
.size = sizeof(UniformInfo)
|
||
};
|
||
|
||
CommandBuffer cmd(Instance);
|
||
vkCmdCopyBuffer(cmd, HostBuffer, UniformSchema, 1, ®ion);
|
||
cmd.execute();
|
||
|
||
InfoSubTexture *subs = (InfoSubTexture*) (data);
|
||
//std::fill((uint64_t*) subs, (uint64_t*) (subs+count), 0);
|
||
|
||
region.dstOffset = sizeof(UniformInfo);
|
||
size_t maxCount = HostBuffer.getSize()/sizeof(InfoSubTexture);
|
||
for(size_t sub = 0; sub < count; sub++, subs++)
|
||
{
|
||
auto iter = SubTextures.find(sub);
|
||
if(iter == SubTextures.end())
|
||
subs->isExist = 0;
|
||
else {
|
||
*subs = iter->second;
|
||
subs->isExist = 1;
|
||
}
|
||
|
||
// Кончилось место
|
||
if((sub+1) % maxCount == 0)
|
||
{
|
||
region.size = maxCount*sizeof(InfoSubTexture);
|
||
vkCmdCopyBuffer(cmd, HostBuffer, UniformSchema, 1, ®ion);
|
||
cmd.execute();
|
||
region.dstOffset += maxCount*sizeof(InfoSubTexture);
|
||
subs = (InfoSubTexture*) (data);
|
||
}
|
||
}
|
||
|
||
if(count % maxCount)
|
||
{
|
||
maxCount = count - (count / maxCount) * maxCount;
|
||
region.size = maxCount*sizeof(InfoSubTexture);
|
||
vkCmdCopyBuffer(cmd, HostBuffer, UniformSchema, 1, ®ion);
|
||
cmd.execute();
|
||
}
|
||
|
||
// for(auto &iter : SubTextures)
|
||
// {
|
||
// TOS_vkAssert(iter.first < count, "Ключи таблицы отсортированы не верно");
|
||
|
||
// subs[iter.first] = iter.second;
|
||
// subs[iter.first].isExist = 1;
|
||
// }
|
||
|
||
HostBuffer.unMapMemory();
|
||
|
||
// Обновляем дескрипторы
|
||
for(size_t iter = 0; iter < AfterUniformChange.size(); iter++)
|
||
{
|
||
if(!AfterUniformChange[iter]())
|
||
{
|
||
AfterUniformChange.erase(AfterUniformChange.begin() + iter);
|
||
iter--;
|
||
}
|
||
}
|
||
|
||
// По выходу из области видимости oldBuffer удалится
|
||
|
||
} else {
|
||
// Просто обновить счётчик анимаций
|
||
* (decltype(UniformInfo::Counter)*) HostBuffer.mapMemory(0, sizeof(UniformInfo::Counter)) = uint32_t(glfwGetTime()*256);
|
||
VkBufferCopy region = {
|
||
.srcOffset = 0,
|
||
.dstOffset = offsetof(UniformInfo, Counter),
|
||
.size = sizeof(UniformInfo::Counter)
|
||
};
|
||
|
||
CommandBuffer cmd(Instance);
|
||
vkCmdCopyBuffer(cmd, HostBuffer, UniformSchema, 1, ®ion);
|
||
cmd.execute();
|
||
|
||
HostBuffer.unMapMemory();
|
||
}
|
||
}
|
||
|
||
ByteBuffer AtlasImage::atlasSchemaUnload() const
|
||
{
|
||
return {};
|
||
}
|
||
|
||
void AtlasImage::atlasSchemaLoad(const ByteBuffer &buff)
|
||
{
|
||
|
||
}
|
||
|
||
const AtlasImage::InfoSubTexture* AtlasImage::atlasGetTextureInfo(uint16_t id)
|
||
{
|
||
auto iter = SubTextures.find(id);
|
||
if(iter == SubTextures.end())
|
||
MAKE_ERROR("Идентификатор не существует");
|
||
|
||
return &iter->second;
|
||
}
|
||
|
||
ArrayImage::ArrayImage(Vulkan *instance, std::filesystem::path directory)
|
||
: Instance(instance)
|
||
{
|
||
CommandBuffer buffer(instance);
|
||
|
||
ByteBuffer buff;
|
||
size_t width, height;
|
||
bool hasAlpha;
|
||
|
||
std::vector<ByteBuffer> images;
|
||
|
||
// Загрузим все текстуры в память и пsроверим их
|
||
for(auto file : std::filesystem::directory_iterator(directory))
|
||
{
|
||
buff = SimpleImage::loadTexture(file, width, height, hasAlpha);
|
||
|
||
if(!Width)
|
||
Width = width;
|
||
|
||
if(width != Width || Width != height)
|
||
{
|
||
MAKE_ERROR("Все текстуры должны быть одинаковые по размеру, " << file.path().filename()
|
||
<< " размер: " << width << " X " << height);
|
||
}
|
||
|
||
if(std::optional<std::vector<std::optional<std::string>>> groups = Str::match(file.path().filename().c_str(), "^(.*)\\..*$"))
|
||
{
|
||
TextureMapping[*groups.value()[1]] = images.size();
|
||
images.push_back(std::move(buff));
|
||
} else
|
||
MAKE_ERROR("Не удалось именовать текстуру " << file.path().filename());
|
||
|
||
}
|
||
|
||
constexpr VkFormat tex_format = VK_FORMAT_B8G8R8A8_UNORM;
|
||
ImageLayout = VK_IMAGE_LAYOUT_GENERAL; // То как будем использовать графический буфер, в данном случае как текстура
|
||
VkFormatProperties props;
|
||
|
||
vkGetPhysicalDeviceFormatProperties(Instance->Graphics.PhysicalDevice, tex_format, &props);
|
||
|
||
VkImageCreateInfo infoImageCreate =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.imageType = VK_IMAGE_TYPE_2D,
|
||
.format = tex_format,
|
||
.extent = { (uint32_t) width, (uint32_t) height, 1 },
|
||
.mipLevels = 1,
|
||
.arrayLayers = uint32_t(images.size()),
|
||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||
.tiling = VK_IMAGE_TILING_MAX_ENUM,
|
||
.usage = VK_IMAGE_USAGE_FLAG_BITS_MAX_ENUM,
|
||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||
.queueFamilyIndexCount = 0,
|
||
.pQueueFamilyIndices = 0,
|
||
.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED
|
||
};
|
||
|
||
VkMemoryAllocateInfo memoryAlloc =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||
.pNext = nullptr,
|
||
.allocationSize = 0,
|
||
.memoryTypeIndex = 0
|
||
};
|
||
|
||
VkMemoryRequirements memoryReqs;
|
||
const VkImageSubresource memorySubres = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, .arrayLayer = 0, };
|
||
|
||
VkImageMemoryBarrier infoImageMemoryBarrier =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||
.pNext = nullptr,
|
||
.srcAccessMask = VK_ACCESS_NONE,
|
||
.dstAccessMask = VK_ACCESS_NONE,
|
||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||
.newLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||
.srcQueueFamilyIndex = 0,
|
||
.dstQueueFamilyIndex = 0,
|
||
.image = VK_NULL_HANDLE,
|
||
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
|
||
};
|
||
|
||
// Создаём временную 2D картинку
|
||
VkImage tempImage;
|
||
VkDeviceMemory tempMemory;
|
||
VkMemoryRequirements memoryReqsTemp = memoryReqs;
|
||
|
||
infoImageCreate.tiling = VK_IMAGE_TILING_LINEAR;
|
||
infoImageCreate.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||
infoImageCreate.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
||
infoImageCreate.arrayLayers = 1;
|
||
vkAssert(!vkCreateImage(Instance->Graphics.Device, &infoImageCreate, nullptr, &tempImage));
|
||
vkGetImageMemoryRequirements(Instance->Graphics.Device, tempImage, &memoryReqsTemp);
|
||
|
||
memoryAlloc.allocationSize = memoryReqsTemp.size;
|
||
memoryAlloc.memoryTypeIndex = Instance->memoryTypeFromProperties(memoryReqsTemp.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||
|
||
vkAssert(!vkAllocateMemory(Instance->Graphics.Device, &memoryAlloc, nullptr, &tempMemory));
|
||
vkAssert(!vkBindImageMemory(Instance->Graphics.Device, tempImage, tempMemory, 0));
|
||
|
||
// Создаём конечную картинку
|
||
if (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)
|
||
infoImageCreate.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||
else
|
||
infoImageCreate.tiling = VK_IMAGE_TILING_LINEAR;
|
||
|
||
infoImageCreate.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||
infoImageCreate.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
||
infoImageCreate.arrayLayers = uint32_t(images.size());
|
||
vkAssert(!vkCreateImage(Instance->Graphics.Device, &infoImageCreate, nullptr, &Image));
|
||
vkGetImageMemoryRequirements(Instance->Graphics.Device, Image, &memoryReqs);
|
||
|
||
memoryAlloc.allocationSize = memoryReqs.size;
|
||
memoryAlloc.memoryTypeIndex = Instance->memoryTypeFromProperties(memoryReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||
|
||
vkAssert(!vkAllocateMemory(Instance->Graphics.Device, &memoryAlloc, nullptr, &Memory));
|
||
vkAssert(!vkBindImageMemory(Instance->Graphics.Device, Image, Memory, 0));
|
||
|
||
// Задаём нужный layout
|
||
infoImageMemoryBarrier.srcAccessMask = VK_ACCESS_NONE;
|
||
infoImageMemoryBarrier.dstAccessMask = VK_ACCESS_NONE;
|
||
infoImageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
||
infoImageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||
infoImageMemoryBarrier.image = Image;
|
||
infoImageMemoryBarrier.subresourceRange.layerCount = uint32_t(images.size());
|
||
|
||
vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||
0, 0, nullptr, 0, nullptr, 1, &infoImageMemoryBarrier);
|
||
|
||
infoImageMemoryBarrier.subresourceRange.layerCount = 1;
|
||
VkSubresourceLayout layout;
|
||
vkGetImageSubresourceLayout(Instance->Graphics.Device, tempImage, &memorySubres, &layout);
|
||
|
||
for(size_t iter = 0; iter < images.size(); iter++)
|
||
{
|
||
// Загружаем по одной картинке
|
||
void *data;
|
||
vkAssert(!vkMapMemory(Instance->Graphics.Device, tempMemory, 0, memoryReqsTemp.size, 0, &data));
|
||
|
||
for (int32_t y = 0; y < Width; y++)
|
||
{
|
||
uint32_t *row = (uint32_t*) (((uint8_t*) data) + layout.rowPitch * y);
|
||
uint32_t *color = ((uint32_t*) images[iter].data()) + Width*y;
|
||
for (uint32_t x = 0; x < Width; x++, row++, color++)
|
||
*row = *color;
|
||
}
|
||
|
||
vkUnmapMemory(Instance->Graphics.Device, tempMemory);
|
||
|
||
infoImageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
|
||
infoImageMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
|
||
infoImageMemoryBarrier.dstAccessMask = VK_ACCESS_NONE;
|
||
infoImageMemoryBarrier.oldLayout = infoImageCreate.initialLayout;
|
||
infoImageCreate.initialLayout = infoImageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||
infoImageMemoryBarrier.image = tempImage;
|
||
|
||
vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||
0, 0, nullptr, 0, nullptr, 1, &infoImageMemoryBarrier);
|
||
|
||
VkImageCopy copy_region =
|
||
{
|
||
.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 },
|
||
.srcOffset = { 0, 0, 0 },
|
||
.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, uint32_t(iter), 1 },
|
||
.dstOffset = { 0, 0, 0 },
|
||
.extent = { Width, Width, 1 },
|
||
};
|
||
|
||
// Настриваем слой на приём данных
|
||
infoImageMemoryBarrier.srcAccessMask = VK_ACCESS_NONE;
|
||
infoImageMemoryBarrier.dstAccessMask = VK_ACCESS_NONE;
|
||
infoImageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||
infoImageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||
infoImageMemoryBarrier.image = Image;
|
||
infoImageMemoryBarrier.subresourceRange.baseArrayLayer = iter;
|
||
vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||
0, 0, nullptr, 0, nullptr, 1, &infoImageMemoryBarrier);
|
||
|
||
vkCmdCopyImage(buffer, tempImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||
Image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region);
|
||
|
||
// Теперь слой будет использоваться как картинка
|
||
infoImageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||
infoImageMemoryBarrier.newLayout = ImageLayout;
|
||
|
||
vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||
0, 0, nullptr, 0, nullptr, 1, &infoImageMemoryBarrier);
|
||
|
||
// Выполняем все команды
|
||
buffer.execute();
|
||
}
|
||
|
||
|
||
// Удаляем не нужную картинку
|
||
vkDestroyImage(Instance->Graphics.Device, tempImage, nullptr);
|
||
vkFreeMemory(Instance->Graphics.Device, tempMemory, nullptr);
|
||
|
||
// infoImageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
|
||
// infoImageMemoryBarrier.subresourceRange.layerCount = uint32_t(images.size());
|
||
// infoImageMemoryBarrier.srcAccessMask = VK_ACCESS_NONE;
|
||
// infoImageMemoryBarrier.dstAccessMask = VK_ACCESS_NONE;
|
||
// infoImageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||
// infoImageMemoryBarrier.newLayout = ImageLayout; // Используем как текстуру
|
||
// infoImageMemoryBarrier.image = Image;
|
||
// vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||
// 0, 0, nullptr, 0, nullptr, 1, &infoImageMemoryBarrier);
|
||
|
||
// buffer.execute();
|
||
|
||
|
||
{
|
||
// Способ чтения картинки
|
||
const VkSamplerCreateInfo ciSampler =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.magFilter = VK_FILTER_NEAREST,
|
||
.minFilter = VK_FILTER_NEAREST,
|
||
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
|
||
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||
.mipLodBias = 0.0f,
|
||
.anisotropyEnable = VK_FALSE,
|
||
.maxAnisotropy = 1,
|
||
.compareEnable = 0,
|
||
.compareOp = VK_COMPARE_OP_NEVER,
|
||
.minLod = 0.0f,
|
||
.maxLod = 0.0f,
|
||
.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,
|
||
.unnormalizedCoordinates = VK_FALSE
|
||
};
|
||
|
||
vkAssert(!vkCreateSampler(Instance->Graphics.Device, &ciSampler, nullptr, &Sampler));
|
||
}
|
||
|
||
{
|
||
// Порядок пикселей
|
||
VkImageViewCreateInfo ciView =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.image = Image,
|
||
.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY,
|
||
.format = tex_format,
|
||
.components =
|
||
{
|
||
VK_COMPONENT_SWIZZLE_B,
|
||
VK_COMPONENT_SWIZZLE_G,
|
||
VK_COMPONENT_SWIZZLE_R,
|
||
VK_COMPONENT_SWIZZLE_A
|
||
},
|
||
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, uint32_t(images.size()) }
|
||
};
|
||
|
||
vkAssert(!vkCreateImageView(Instance->Graphics.Device, &ciView, nullptr, &View));
|
||
}
|
||
}
|
||
|
||
ArrayImage::~ArrayImage()
|
||
{
|
||
if(Instance && Instance->Graphics.Device)
|
||
{
|
||
Instance->beforeDraw([image = Image, memory = Memory,
|
||
sampler = Sampler, view = View](Vulkan *instance)
|
||
{
|
||
if(image)
|
||
vkDestroyImage(instance->Graphics.Device, image, nullptr);
|
||
if(memory)
|
||
vkFreeMemory(instance->Graphics.Device, memory, nullptr);
|
||
if(sampler)
|
||
vkDestroySampler(instance->Graphics.Device, sampler, nullptr);
|
||
if(view)
|
||
vkDestroyImageView(instance->Graphics.Device, view, nullptr);
|
||
});
|
||
}
|
||
}
|
||
|
||
uint16_t ArrayImage::getTextureId(const std::string &name)
|
||
{
|
||
auto iter = TextureMapping.find(name);
|
||
if(iter == TextureMapping.end())
|
||
return 0;
|
||
|
||
return iter->second;
|
||
}
|
||
|
||
FontAtlas::FontAtlas(Vulkan *instance)
|
||
: AtlasImage(instance)
|
||
{
|
||
FT_Init_FreeType(&FontLibrary);
|
||
|
||
const VkSamplerCreateInfo ciSampler =
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.magFilter = VK_FILTER_LINEAR,
|
||
.minFilter = VK_FILTER_NEAREST,
|
||
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
|
||
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||
.mipLodBias = 0.0f,
|
||
.anisotropyEnable = VK_FALSE,
|
||
.maxAnisotropy = 1,
|
||
.compareEnable = 0,
|
||
.compareOp = VK_COMPARE_OP_NEVER,
|
||
.minLod = 0.0f,
|
||
.maxLod = 0.0f,
|
||
.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,
|
||
.unnormalizedCoordinates = VK_FALSE
|
||
};
|
||
|
||
changeSampler(&ciSampler);
|
||
}
|
||
|
||
FontAtlas::~FontAtlas()
|
||
{
|
||
FontList.clear();
|
||
|
||
if(FontLibrary)
|
||
FT_Done_FreeType(FontLibrary);
|
||
}
|
||
|
||
void FontAtlas::clearInvalidGlyphs()
|
||
{
|
||
std::vector<uint64_t> toDelete;
|
||
for(auto &iter : CharToInfo)
|
||
if(!iter.second.IsValid)
|
||
toDelete.push_back(iter.first);
|
||
|
||
for(auto &iter : toDelete)
|
||
CharToInfo.erase(CharToInfo.find(iter));
|
||
|
||
LastUpdate += 1;
|
||
}
|
||
|
||
void FontAtlas::pushFont(const std::variant<std::filesystem::path, ByteBuffer> &font)
|
||
{
|
||
FontList.push_back({});
|
||
auto &face = FontList.back();
|
||
if(font.index() == 0)
|
||
face.Data = std::ifstream(std::get<std::filesystem::path>(font));
|
||
else
|
||
face.Data = std::move(std::get<ByteBuffer>(font));
|
||
|
||
FT_New_Memory_Face(FontLibrary, face.Data.data(), face.Data.size(), 0, &face.Obj);
|
||
|
||
clearInvalidGlyphs();
|
||
}
|
||
|
||
void FontAtlas::setFontList(const std::vector<std::variant<std::filesystem::path, ByteBuffer>> &fonts)
|
||
{
|
||
atlasClear();
|
||
FontList.clear();
|
||
for(auto &obj : fonts)
|
||
{
|
||
FontList.push_back({});
|
||
auto &face = FontList.back();
|
||
if(obj.index() == 0)
|
||
face.Data = std::ifstream(std::get<std::filesystem::path>(obj));
|
||
else
|
||
face.Data = std::move(std::get<ByteBuffer>(obj));
|
||
|
||
FT_New_Memory_Face(FontLibrary, face.Data.data(), face.Data.size(), 0, &face.Obj);
|
||
}
|
||
|
||
clearInvalidGlyphs();
|
||
}
|
||
|
||
FontAtlas::GlyphInfo FontAtlas::getGlyph(UChar wc, uint16_t size)
|
||
{
|
||
uint64_t glyphId = (uint64_t(wc) << sizeof(uint16_t)*8) | size;
|
||
auto iter = CharToInfo.find(glyphId);
|
||
if(iter == CharToInfo.end())
|
||
{
|
||
for (const Face &face : FontList)
|
||
{
|
||
GlyphInfo info;
|
||
|
||
{
|
||
FT_Set_Pixel_Sizes(face.Obj, 0, size);
|
||
FT_Load_Char(face.Obj, wc, FT_LOAD_RENDER);
|
||
|
||
FT_UInt glyph_index = FT_Get_Char_Index(face.Obj, wc);
|
||
|
||
if (glyph_index == 0) // Нет символа в шрифте, посмотрим в других
|
||
continue;
|
||
|
||
FT_Load_Glyph(face.Obj, glyph_index, FT_LOAD_DEFAULT);
|
||
FT_Render_Glyph(face.Obj->glyph, FT_RENDER_MODE_NORMAL);
|
||
FT_Glyph glyph;
|
||
FT_Get_Glyph(face.Obj->glyph, &glyph);
|
||
|
||
FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_LCD, 0, 1);
|
||
FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph) glyph;
|
||
FT_Bitmap bitmap = bitmap_glyph->bitmap;
|
||
|
||
try {
|
||
ByteBuffer data((bitmap.width+2)*(bitmap.rows+2)*sizeof(uint32_t));
|
||
|
||
std::fill((uint32_t*) data.data(), (uint32_t*) (data.data() + data.size()), 0xffffff);
|
||
|
||
uint8_t *ptrFrom = (uint8_t*) bitmap.buffer;
|
||
uint32_t *ptrTo = ((uint32_t*) data.data()) + (bitmap.width+2);
|
||
for (int y = 0; y < bitmap.rows; y++)
|
||
{
|
||
ptrTo++;
|
||
for(int x = 0; x < bitmap.width; x++, ptrFrom++, ptrTo++)
|
||
*ptrTo |= uint32_t(*ptrFrom) << 24;
|
||
|
||
ptrTo++;
|
||
}
|
||
|
||
info.IsValid = true;
|
||
info.Width = glyph->advance.x >> 16;
|
||
info.Height = bitmap_glyph->top;
|
||
info.PosX = bitmap_glyph->left;
|
||
info.PosY = bitmap_glyph->top-int(bitmap.rows);
|
||
|
||
if(wc == L'?')
|
||
info.IsValid = false;
|
||
|
||
info.TexId = atlasAddTexture(bitmap.width+2, bitmap.rows+2);
|
||
atlasChangeTextureData(info.TexId, (uint32_t*) data.data());
|
||
} catch(...) {
|
||
FT_Done_Glyph(glyph);
|
||
throw;
|
||
}
|
||
|
||
FT_Done_Glyph(glyph);
|
||
}
|
||
|
||
return CharToInfo[glyphId] = info;
|
||
}
|
||
} else
|
||
return iter->second;
|
||
|
||
if(wc != L'?')
|
||
return getGlyph(L'?', size);
|
||
|
||
GlyphInfo info;
|
||
|
||
info.IsValid = false;
|
||
info.TexId = 65535;
|
||
info.Width = size;
|
||
info.Height = size;
|
||
info.PosX = 0;
|
||
info.PosY = 0;
|
||
|
||
return CharToInfo[glyphId] = info;
|
||
}
|
||
|
||
|
||
|
||
PipelineVF::PipelineVF(std::shared_ptr<DescriptorLayout> layout, const std::string &vertex,
|
||
const std::string &fragment)
|
||
: Pipeline(layout), PathVertex(vertex), PathFragment(fragment)
|
||
{
|
||
}
|
||
|
||
PipelineVF::~PipelineVF() = default;
|
||
|
||
void PipelineVF::init(Vulkan *instance)
|
||
{
|
||
if(!ShaderVertex)
|
||
ShaderVertex = instance->createShader(getResource(PathVertex)->makeView());
|
||
|
||
if(!ShaderFragment)
|
||
ShaderFragment = instance->createShader(getResource(PathFragment)->makeView());
|
||
|
||
Settings.ShaderStages =
|
||
{
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
||
.module = *ShaderVertex,
|
||
.pName = "main",
|
||
.pSpecializationInfo = nullptr
|
||
}, {
|
||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||
.module = *ShaderFragment,
|
||
.pName = "main",
|
||
.pSpecializationInfo = nullptr
|
||
}
|
||
};
|
||
|
||
Pipeline::init(instance);
|
||
}
|
||
|
||
PipelineVGF::PipelineVGF(std::shared_ptr<DescriptorLayout> layout, const std::string &vertex,
|
||
const std::string &geometry, const std::string &fragment)
|
||
: Pipeline(layout), PathVertex(vertex), PathGeometry(geometry), PathFragment(fragment)
|
||
{
|
||
// Settings.ShaderVertexBindings =
|
||
// {
|
||
// {
|
||
// .binding = 0,
|
||
// .stride = sizeof(Client::Chunk::Vertex),
|
||
// .inputRate = VK_VERTEX_INPUT_RATE_VERTEX
|
||
// }
|
||
// };
|
||
|
||
// Settings.ShaderVertexAttribute =
|
||
// {
|
||
// {
|
||
// .location = 0,
|
||
// .binding = 0,
|
||
// .format = VK_FORMAT_R32_UINT,
|
||
// .offset = 0
|
||
// }
|
||
// };
|
||
}
|
||
|
||
PipelineVGF::~PipelineVGF() = default;
|
||
|
||
void PipelineVGF::init(Vulkan *instance)
|
||
{
|
||
if(!ShaderVertex)
|
||
ShaderVertex = instance->createShader(getResource(PathVertex)->makeView());
|
||
|
||
if(!ShaderGeometry)
|
||
ShaderGeometry = instance->createShader(getResource(PathGeometry)->makeView());
|
||
|
||
if(!ShaderFragment)
|
||
ShaderFragment = instance->createShader(getResource(PathFragment)->makeView());
|
||
|
||
Settings.ShaderStages =
|
||
{
|
||
{
|
||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
||
.module = *ShaderVertex,
|
||
.pName = "main",
|
||
.pSpecializationInfo = nullptr
|
||
}, {
|
||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.stage = VK_SHADER_STAGE_GEOMETRY_BIT,
|
||
.module = *ShaderGeometry,
|
||
.pName = "main",
|
||
.pSpecializationInfo = nullptr
|
||
}, {
|
||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||
.pNext = nullptr,
|
||
.flags = 0,
|
||
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||
.module = *ShaderFragment,
|
||
.pName = "main",
|
||
.pSpecializationInfo = nullptr
|
||
}
|
||
};
|
||
|
||
Pipeline::init(instance);
|
||
}
|
||
|
||
}
|