Графические конвейеры для вокселей и нод

This commit is contained in:
2025-02-13 22:22:10 +06:00
parent a27f055af8
commit 8c13938b06
24 changed files with 1239 additions and 65 deletions

View File

@@ -9,6 +9,26 @@
namespace LV::Client {
struct GlobalTime {
uint32_t Seconds : 22, Sub : 10;
GlobalTime() = default;
GlobalTime(double gTime) {
Seconds = int(gTime);
Sub = (gTime-int(gTime))*1024;
}
GlobalTime& operator=(double gTime) {
Seconds = int(gTime);
Sub = (gTime-int(gTime))*1024;
return *this;
}
operator double() const {
return double(Seconds) + double(Sub)/1024.;
}
};
struct VoxelCube {
DefVoxelId_c VoxelId;
Pos::Local256 Left, Right;
@@ -129,7 +149,7 @@ public:
virtual ~IServerSession();
virtual void atFreeDrawTime() = 0;
virtual void atFreeDrawTime(GlobalTime gTime, float dTime) = 0;
};

View File

@@ -2,13 +2,13 @@
#include "Client/Abstract.hpp"
#include "Common/Abstract.hpp"
#include "Common/Net.hpp"
#include <GLFW/glfw3.h>
#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/this_coro.hpp>
#include <boost/date_time/posix_time/posix_time_duration.hpp>
#include <functional>
#include <memory>
#include <Common/Packets.hpp>
#include <GLFW/glfw3.h>
#include <glm/ext.hpp>
@@ -162,17 +162,24 @@ void ServerSession::onCursorPosChange(int32_t width, int32_t height) {
}
void ServerSession::onCursorMove(float xMove, float yMove) {
glm::vec3 deltaPYR;
static constexpr float PI = glm::pi<float>(), PI2 = PI*2, PI_HALF = PI/2, PI_DEG = PI/180;
deltaPYR.x = std::clamp(PYR.x + yMove*PI_DEG, -PI_HALF+PI_DEG, PI_HALF-PI_DEG)-PYR.x;
deltaPYR.y = std::fmod(PYR.y + xMove*PI_DEG, PI2)-PYR.y;
deltaPYR.z = 0;
double gTime = GTime;
float deltaTime = 1-std::min<float>(gTime-PYR_At, 1/PYR_TIME_DELTA)*PYR_TIME_DELTA;
PYR_At = GTime;
PYR += deltaPYR;
PYR_Offset = deltaPYR+deltaTime*PYR_Offset;
glm::vec3 front = Camera.Quat*glm::vec3(0.0f, 0.0f, -1.0f);
}
void ServerSession::onFrameRendering() {
}
void ServerSession::onFrameRenderEnd() {
}
void ServerSession::onCursorBtn(ISurfaceEventListener::EnumCursorBtn btn, bool state) {
}
@@ -187,8 +194,8 @@ void ServerSession::onJoystick() {
}
void ServerSession::atFreeDrawTime() {
void ServerSession::atFreeDrawTime(GlobalTime gTime, float dTime) {
GTime = gTime;
}
coro<> ServerSession::run() {

View File

@@ -37,6 +37,12 @@ class ServerSession : public AsyncObject, public IServerSession, public ISurface
boost::lockfree::spsc_queue<std::unique_ptr<ParsedPacket>> NetInputPackets;
//
glm::vec3 PYR = glm::vec3(0), PYR_Offset = glm::vec3(0);
double PYR_At = 0;
static constexpr float PYR_TIME_DELTA = 30;
GlobalTime GTime;
public:
// Нужен сокет, на котором только что был согласован игровой протокол (asyncInitGameProtocol)
ServerSession(asio::io_context &ioc, std::unique_ptr<Net::AsyncSocket> &&socket, IRenderSession *rs = nullptr)
@@ -70,14 +76,12 @@ public:
virtual void onChangeFocusState(bool isFocused) override;
virtual void onCursorPosChange(int32_t width, int32_t height) override;
virtual void onCursorMove(float xMove, float yMove) override;
virtual void onFrameRendering() override;
virtual void onFrameRenderEnd() override;
virtual void onCursorBtn(EnumCursorBtn btn, bool state) override;
virtual void onKeyboardBtn(int btn, int state) override;
virtual void onJoystick() override;
virtual void atFreeDrawTime() override;
virtual void atFreeDrawTime(GlobalTime gTime, float dTime) override;
private:
coro<> run();

View File

@@ -87,17 +87,6 @@ Vulkan::Vulkan(asio::io_context &ioc)
auto useLock = Game.UseLock.lock();
run();
GuardLock.reset();
if(Game.Session) {
Game.Session->shutdown(EnumDisconnect::ByInterface);
Game.Session = nullptr;
Game.RSession = nullptr;
}
if(Game.Server) {
Game.Server->GS.shutdown("Завершение работы из-за остановки клиента");
Game.Server = nullptr;
}
});
}
@@ -132,8 +121,14 @@ void Vulkan::run()
assert(!vkCreateSemaphore(Graphics.Device, &semaphoreCreateInfo, NULL, &SemaphoreDrawComplete));
while(!NeedShutdown)
double prevTime = glfwGetTime();
while(!NeedShutdown
|| (Game.Session && Game.Session->isConnected())
|| (Game.Server && Game.Server->GS.isAlive()))
{
float dTime = glfwGetTime()-prevTime;
prevTime = glfwGetTime();
Screen.State = DrawState::Begin;
{
std::lock_guard lock(Screen.BeforeDrawMtx);
@@ -144,8 +139,16 @@ void Vulkan::run()
}
}
if(glfwWindowShouldClose(Graphics.Window)) {
if(!NeedShutdown && glfwWindowShouldClose(Graphics.Window)) {
NeedShutdown = true;
if(Game.Session) {
Game.Session->shutdown(EnumDisconnect::ByInterface);
}
if(Game.Server) {
Game.Server->GS.shutdown("Завершение работы из-за остановки клиента");
}
}
if(Game.Session) {
@@ -168,6 +171,10 @@ void Vulkan::run()
// if(CallBeforeDraw)
// CallBeforeDraw(this);
if(Game.RSession) {
Game.RSession->beforeDraw();
}
glfwPollEvents();
VkResult err;
@@ -251,8 +258,17 @@ void Vulkan::run()
vkCmdSetScissor(Graphics.CommandBufferRender, 0, 1, &scissor);
}
// if(CallOnDraw)
// CallOnDraw(this, 0, Graphics.CommandBufferRender);
GlobalTime gTime = glfwGetTime();
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);
@@ -400,6 +416,10 @@ void Vulkan::run()
assert(!err);
}
if(Game.Session) {
Game.Session->atFreeDrawTime(gTime, dTime);
}
assert(!vkQueueWaitIdle(Graphics.DeviceQueueGraphic));
vkDeviceWaitIdle(Graphics.Device);
@@ -410,6 +430,10 @@ void Vulkan::run()
vkDestroySemaphore(Graphics.Device, SemaphoreDrawComplete, nullptr);
Graphics.ThisThread = std::thread::id();
Game.Session = nullptr;
Game.RSession = nullptr;
Game.Server = nullptr;
}
void Vulkan::glfwCallbackError(int error, const char *description)
@@ -1308,7 +1332,7 @@ void Vulkan::initNextSettings()
} else {
for(const std::shared_ptr<IVulkanDependent> &dependent : ROS_Dependents)
{
if(dependent->needRebuild() != EnumRebuildType::None || dynamic_cast<Pipeline*>(dependent.get()))
if(/*dependent->needRebuild() != EnumRebuildType::None || */ dynamic_cast<Pipeline*>(dependent.get()))
dependent->free(this);
}
@@ -1842,6 +1866,7 @@ bool Vulkan::needFullVulkanRebuild()
std::shared_ptr<ShaderModule> Vulkan::createShader(const ByteBuffer &data)
{
assert(Graphics.Device);
assert(data.size());
std::shared_ptr<ShaderModule> module = std::make_shared<ShaderModule>(data);
std::dynamic_pointer_cast<IVulkanDependent>(module)->init(this);
ROS_Dependents.insert(module);
@@ -1967,7 +1992,8 @@ void Vulkan::gui_MainMenu() {
if(ConnectionProgress.Socket) {
std::unique_ptr<Net::AsyncSocket> sock = std::move(ConnectionProgress.Socket);
Game.RSession = std::make_unique<VulkanRenderSession>(this);
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);
@@ -1989,19 +2015,18 @@ void Vulkan::gui_ConnectedToServer() {
}
EnumRebuildType IVulkanDependent::needRebuild() { return EnumRebuildType::None; }
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);
}
}
// for(const std::shared_ptr<IVulkanDependent> &dependent : ROS_Dependents)
// {
// if(dependent->needRebuild() != EnumRebuildType::None)
// {
// dependent->free(this);
// dependent->init(this);
// }
// }
}
@@ -2412,11 +2437,6 @@ void Pipeline::init(Vulkan *instance)
assert(!vkCreateGraphicsPipelines(instance->Graphics.Device, VK_NULL_HANDLE, 1, &pipeline, nullptr, &PipelineObj));
}
EnumRebuildType Pipeline::needRebuild()
{
return PipelineObj ? EnumRebuildType::None : EnumRebuildType::Urgently;
}
// Shader
@@ -4418,4 +4438,115 @@ FontAtlas::GlyphInfo FontAtlas::getGlyph(UChar wc, uint16_t size)
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->createShaderFromFile(PathVertex);
if(!ShaderFragment)
ShaderFragment = instance->createShaderFromFile(PathFragment);
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->createShaderFromFile(PathVertex);
if(!ShaderGeometry)
ShaderGeometry = instance->createShaderFromFile(PathGeometry);
if(!ShaderFragment)
ShaderFragment = instance->createShaderFromFile(PathFragment);
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);
}
}

View File

@@ -234,11 +234,11 @@ public:
std::queue<std::function<void(Vulkan*)>> BeforeDraw;
DrawState State = DrawState::Begin;
} Screen;
struct {
DestroyLock UseLock;
std::thread MainThread;
std::unique_ptr<VulkanRenderSession> RSession;
std::shared_ptr<VulkanRenderSession> RSession;
std::unique_ptr<ServerSession> Session;
std::list<void (Vulkan::*)()> ImGuiInterfaces;
@@ -358,17 +358,10 @@ public:
void gui_ConnectedToServer();
};
enum class EnumRebuildType {
None,
Need,
Urgently
};
class IVulkanDependent : public std::enable_shared_from_this<IVulkanDependent> {
protected:
virtual void free(Vulkan *instance) = 0;
virtual void init(Vulkan *instance) = 0;
virtual EnumRebuildType needRebuild();
friend Vulkan;
friend Pipeline;
@@ -449,8 +442,6 @@ protected:
virtual void free(Vulkan *instance) override;
virtual void init(Vulkan *instance) override;
// Если необходимо изменить графический конвейер, будет вызвано free и init
virtual EnumRebuildType needRebuild() override;
public:
Pipeline(std::shared_ptr<DescriptorLayout> layout);
@@ -1000,5 +991,29 @@ public:
size_t getLastUpdate() { return LastUpdate; }
};
class PipelineVF : public Pipeline {
std::string PathVertex, PathFragment;
std::shared_ptr<ShaderModule> ShaderVertex, ShaderFragment;
protected:
virtual void init(Vulkan *instance) override;
public:
PipelineVF(std::shared_ptr<DescriptorLayout> layout, const std::string &vertex, const std::string &fragment);
virtual ~PipelineVF();
};
class PipelineVGF : public Pipeline {
std::string PathVertex, PathGeometry, PathFragment;
std::shared_ptr<ShaderModule> ShaderVertex, ShaderGeometry, ShaderFragment;
protected:
virtual void init(Vulkan *instance) override;
public:
PipelineVGF(std::shared_ptr<DescriptorLayout> layout, const std::string &vertex, const std::string &geometry, const std::string &fragment);
virtual ~PipelineVGF();
};
} /* namespace TOS::Navie::VK */

View File

@@ -1,19 +1,464 @@
#include "VulkanRenderSession.hpp"
#include <vulkan/vulkan_core.h>
namespace LV::Client::VK {
VulkanRenderSession::VulkanRenderSession(VK::Vulkan *vkInst)
: VkInst(vkInst),
MainAtlas(vkInst)
VulkanRenderSession::VulkanRenderSession()
{
assert(VkInst);
}
VulkanRenderSession::~VulkanRenderSession() {
}
void VulkanRenderSession::free(Vulkan *instance) {
if(instance && instance->Graphics.Device)
{
if(VoxelOpaquePipeline)
vkDestroyPipeline(instance->Graphics.Device, VoxelOpaquePipeline, nullptr);
if(VoxelTransparentPipeline)
vkDestroyPipeline(instance->Graphics.Device, VoxelTransparentPipeline, nullptr);
if(NodeStaticOpaquePipeline)
vkDestroyPipeline(instance->Graphics.Device, NodeStaticOpaquePipeline, nullptr);
if(NodeStaticTransparentPipeline)
vkDestroyPipeline(instance->Graphics.Device, NodeStaticTransparentPipeline, nullptr);
if(MainAtlas_LightMap_PipelineLayout)
vkDestroyPipelineLayout(instance->Graphics.Device, MainAtlas_LightMap_PipelineLayout, nullptr);
if(MainAtlasDescLayout)
vkDestroyDescriptorSetLayout(instance->Graphics.Device, MainAtlasDescLayout, nullptr);
if(LightMapDescLayout)
vkDestroyDescriptorSetLayout(instance->Graphics.Device, LightMapDescLayout, nullptr);
}
VoxelOpaquePipeline = VK_NULL_HANDLE;
VoxelTransparentPipeline = VK_NULL_HANDLE;
NodeStaticOpaquePipeline = VK_NULL_HANDLE;
NodeStaticTransparentPipeline = VK_NULL_HANDLE;
MainAtlas_LightMap_PipelineLayout = VK_NULL_HANDLE;
MainAtlasDescLayout = VK_NULL_HANDLE;
LightMapDescLayout = VK_NULL_HANDLE;
}
void VulkanRenderSession::init(Vulkan *instance) {
if(VkInst != instance) {
VkInst = instance;
}
// Разметка дескрипторов
if(!MainAtlasDescLayout) {
std::vector<VkDescriptorSetLayoutBinding> 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_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.pImmutableSamplers = nullptr
}
};
const VkDescriptorSetLayoutCreateInfo descriptorLayout =
{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.bindingCount = (uint32_t) shaderLayoutBindings.size(),
.pBindings = shaderLayoutBindings.data()
};
assert(!vkCreateDescriptorSetLayout(
instance->Graphics.Device, &descriptorLayout, nullptr, &MainAtlasDescLayout));
}
if(!LightMapDescLayout) {
std::vector<VkDescriptorSetLayoutBinding> 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_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.pImmutableSamplers = nullptr
}
};
const VkDescriptorSetLayoutCreateInfo descriptorLayout =
{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.bindingCount = (uint32_t) shaderLayoutBindings.size(),
.pBindings = shaderLayoutBindings.data()
};
assert(!vkCreateDescriptorSetLayout( instance->Graphics.Device, &descriptorLayout, nullptr, &LightMapDescLayout));
}
std::vector<VkPushConstantRange> worldWideShaderPushConstants =
{
{
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT,
.offset = 0,
.size = uint32_t(sizeof(WorldPCO))
}
};
// Разметка графических конвейеров
if(!MainAtlas_LightMap_PipelineLayout) {
std::vector<VkDescriptorSetLayout> layouts =
{
MainAtlasDescLayout,
LightMapDescLayout
};
const VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo =
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.setLayoutCount = (uint32_t) layouts.size(),
.pSetLayouts = layouts.data(),
.pushConstantRangeCount = (uint32_t) worldWideShaderPushConstants.size(),
.pPushConstantRanges = worldWideShaderPushConstants.data()
};
assert(!vkCreatePipelineLayout(instance->Graphics.Device, &pPipelineLayoutCreateInfo, nullptr, &MainAtlas_LightMap_PipelineLayout));
}
// Настройка мультисемплинга
// Может нужно будет в будущем связать с настройками главного буфера
VkPipelineMultisampleStateCreateInfo multisample =
{
.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
};
VkPipelineCacheCreateInfo infoPipelineCache;
memset(&infoPipelineCache, 0, sizeof(infoPipelineCache));
infoPipelineCache.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
// Конвейеры для вокселей
if(!VoxelOpaquePipeline || !VoxelTransparentPipeline
|| !NodeStaticOpaquePipeline || !NodeStaticTransparentPipeline)
{
// Для статичных непрозрачных и полупрозрачных вокселей
if(!VoxelShaderVertex)
VoxelShaderVertex = VkInst->createShaderFromFile("assets/shaders/chunk/voxel.vert.bin");
if(!VoxelShaderGeometry)
VoxelShaderGeometry = VkInst->createShaderFromFile("assets/shaders/chunk/voxel.geom.bin");
if(!VoxelShaderFragmentOpaque)
VoxelShaderFragmentOpaque = VkInst->createShaderFromFile("assets/shaders/chunk/voxel_opaque.frag.bin");
if(!VoxelShaderFragmentTransparent)
VoxelShaderFragmentTransparent = VkInst->createShaderFromFile("assets/shaders/chunk/voxel_transparent.frag.bin");
// Конвейер шейдеров
std::vector<VkPipelineShaderStageCreateInfo> shaderStages =
{
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = *VoxelShaderVertex,
.pName = "main",
.pSpecializationInfo = nullptr
}, {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.stage = VK_SHADER_STAGE_GEOMETRY_BIT,
.module = *VoxelShaderGeometry,
.pName = "main",
.pSpecializationInfo = nullptr
}, {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = *VoxelShaderFragmentOpaque,
.pName = "main",
.pSpecializationInfo = nullptr
}
};
// Вершины шейдера
// Настройка формата вершин шейдера
std::vector<VkVertexInputBindingDescription> shaderVertexBindings =
{
{
.binding = 0,
.stride = sizeof(VoxelVertexPoint),
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX
}
};
std::vector<VkVertexInputAttributeDescription> shaderVertexAttribute =
{
{
.location = 0,
.binding = 0,
.format = VK_FORMAT_R32G32B32_UINT,
.offset = 0
}
};
VkPipelineVertexInputStateCreateInfo createInfoVertexInput =
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.vertexBindingDescriptionCount = (uint32_t) shaderVertexBindings.size(),
.pVertexBindingDescriptions = shaderVertexBindings.data(),
.vertexAttributeDescriptionCount = (uint32_t) shaderVertexAttribute.size(),
.pVertexAttributeDescriptions = shaderVertexAttribute.data()
};
// Топология вершин на входе (треугольники, линии, точки)
VkPipelineInputAssemblyStateCreateInfo ia =
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
.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
};
// Настройки растеризатора
VkPipelineRasterizationStateCreateInfo rasterization =
{
.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
};
// Тест буфера глубины и трафарета
VkPipelineDepthStencilStateCreateInfo depthStencil =
{
.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 = 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
},
.minDepthBounds = 0.0f,
.maxDepthBounds = 0.0f
};
// Логика смешивания цветов
std::vector<VkPipelineColorBlendAttachmentState> 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
}
};
VkPipelineColorBlendStateCreateInfo cb =
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_CLEAR,
.attachmentCount = (uint32_t) colorBlend.size(),
.pAttachments = colorBlend.data(),
.blendConstants = {0.f, 0.f, 0.f, 0.f}
};
// Настройки конвейера, которые могут быть изменены без пересоздания конвейера
std::vector<VkDynamicState> dynamicStates =
{
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicState =
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.dynamicStateCount = (uint32_t) dynamicStates.size(),
.pDynamicStates = dynamicStates.data(),
};
VkGraphicsPipelineCreateInfo pipeline =
{
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.stageCount = (uint32_t) shaderStages.size(),
.pStages = shaderStages.data(),
.pVertexInputState = &createInfoVertexInput,
.pInputAssemblyState = &ia,
.pTessellationState = nullptr,
.pViewportState = &vp,
.pRasterizationState = &rasterization,
.pMultisampleState = &multisample,
.pDepthStencilState = &depthStencil,
.pColorBlendState = &cb,
.pDynamicState = &dynamicState,
.layout = MainAtlas_LightMap_PipelineLayout,
.renderPass = instance->Graphics.RenderPass,
.subpass = 0,
.basePipelineHandle = nullptr,
.basePipelineIndex = 0
};
if(!VoxelOpaquePipeline)
assert(!vkCreateGraphicsPipelines(instance->Graphics.Device, VK_NULL_HANDLE, 1, &pipeline, nullptr, &VoxelOpaquePipeline));
if(!VoxelTransparentPipeline) {
shaderStages[2].module = *VoxelShaderFragmentTransparent,
colorBlend[0] =
{
.blendEnable = VK_TRUE,
.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.alphaBlendOp = VK_BLEND_OP_ADD,
.colorWriteMask = 0xf
};
assert(!vkCreateGraphicsPipelines(instance->Graphics.Device, VK_NULL_HANDLE, 1, &pipeline, nullptr, &VoxelTransparentPipeline));
}
// Для статичных непрозрачных и полупрозрачных нод
if(!NodeShaderVertex)
NodeShaderVertex = VkInst->createShaderFromFile("assets/shaders/chunk/node.vert.bin");
if(!NodeShaderGeometry)
NodeShaderGeometry = VkInst->createShaderFromFile("assets/shaders/chunk/node.geom.bin");
if(!NodeShaderFragmentOpaque)
NodeShaderFragmentOpaque = VkInst->createShaderFromFile("assets/shaders/chunk/node_opaque.frag.bin");
if(!NodeShaderFragmentTransparent)
NodeShaderFragmentTransparent = VkInst->createShaderFromFile("assets/shaders/chunk/node_transparent.frag.bin");
ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
shaderStages[0].module = *NodeShaderVertex;
shaderStages[1].module = *NodeShaderGeometry;
shaderStages[2].module = *NodeShaderFragmentOpaque;
colorBlend[0] =
{
.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
};
if(!NodeStaticOpaquePipeline) {
assert(!vkCreateGraphicsPipelines(instance->Graphics.Device, VK_NULL_HANDLE,
1, &pipeline, nullptr, &NodeStaticOpaquePipeline));
}
if(!NodeStaticTransparentPipeline) {
shaderStages[2].module = *NodeShaderFragmentTransparent;
colorBlend[0] =
{
.blendEnable = VK_TRUE,
.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.alphaBlendOp = VK_BLEND_OP_ADD,
.colorWriteMask = 0xf
};
assert(!vkCreateGraphicsPipelines(instance->Graphics.Device, VK_NULL_HANDLE,
1, &pipeline, nullptr, &NodeStaticTransparentPipeline));
}
}
}
void VulkanRenderSession::onDefTexture(TextureId_c id, std::vector<std::byte> &&info) {
}
@@ -60,4 +505,12 @@ void VulkanRenderSession::setCameraPos(WorldId_c worldId, Pos::Object pos, glm::
Quat = quat;
}
void VulkanRenderSession::beforeDraw() {
}
void VulkanRenderSession::drawWorld(GlobalTime gTime, float dTime, VkCommandBuffer drawCmd) {
}
}

View File

@@ -1,23 +1,124 @@
#pragma once
#include "Client/Abstract.hpp"
#include "Common/Abstract.hpp"
#include <Client/Vulkan/Vulkan.hpp>
#include <glm/ext/matrix_transform.hpp>
/*
У движка есть один текстурный атлас VK_IMAGE_VIEW_TYPE_2D_ARRAY(RGBA_UINT) и к нему Storage с инфой о положении текстур
Это общий для всех VkDescriptorSetLayout и VkDescriptorSet
Для отрисовки вокселей используется карта освещения VK_IMAGE_VIEW_TYPE_2D_ARRAY(RGB_UINT), разделённая по прямоугольным плоскостям
Разрешение у этих карт 1м/16
-- Для всего остального 3д карты освещённости по чанкам в разрешении 1м. 16^3 = 4096 текселей --
*/
/*
Самые критичные случаи
Для вокселей это чередование в шахматном порядке присутствия и отсутствия вокселей
Это 8'388'608 вокселя в чанке, общей площадью на картах освещения (256^3/2 *4 *6) 201'326'592 текселей или текстура размером 16к^2
Понадобится переиспользование одинаковых участков освещённости
Если чанк заполнен слоями вокселей в шахматном порядке по вертикале ((257^2+257*2*4)*128) 8'717'440 текселей или текстура размером 4к^2
*/
namespace LV::Client::VK {
class VulkanRenderSession : public IRenderSession {
VK::Vulkan *VkInst;
struct WorldPCO {
glm::mat4 ProjView, Model;
};
static_assert(sizeof(WorldPCO) == 128);
/*
Воксели рендерятся точками, которые распаковываются в квадратные плоскости
В чанке по оси 256 вокселей, и 257 позиций вершин (включая дальнюю границу чанка)
9 бит на позицию *3 оси = 27 бит
Указание материала 16 бит
*/
struct VoxelVertexPoint {
uint32_t
FX : 9, FY : 9, FZ : 9, // Позиция
Place : 3, // Положение распространения xz, xy, zy, и обратные
N1 : 1, // Не занято
LS : 1, // Масштаб карты освещения (1м/16 или 1м)
TX : 8, TY : 8, // Размер+1
VoxMtl : 16, // Материал вокселя DefVoxelId_t
LU : 14, LV : 14, // Позиция на карте освещения
N2 : 2; // Не занято
};
/*
Из-за карт освещения индексов не будет
Максимальный размер меша 14^3 м от центра ноды
Координатное пространство то же, что и у вокселей + 8 позиций с двух сторон
Рисуется полигонами
*/
struct NodeVertexStatic {
uint32_t
FX : 9, FY : 9, FZ : 9, // Позиция -112 ~ 369 / 16
N1 : 4, // Не занято
LS : 1, // Масштаб карты освещения (1м/16 или 1м)
Tex : 18, // Текстура
N2 : 14, // Не занято
TU : 16, TV : 16; // UV на текстуре
};
class VulkanRenderSession : public IRenderSession, public IVulkanDependent {
VK::Vulkan *VkInst = nullptr;
// Доступ к миру на стороне клиента
IServerSession *ServerSession = nullptr;
// Положение камеры
WorldId_c WorldId;
Pos::Object Pos;
glm::quat Quat;
VK::AtlasImage MainAtlas;
/*
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, Текстурный атлас
.binding = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, Данные к атласу
*/
VkDescriptorSetLayout MainAtlasDescLayout = VK_NULL_HANDLE;
/*
.binding = 2,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, Карта освещения
.binding = 3,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, Информация о размерах карты для приведения размеров
*/
VkDescriptorSetLayout LightMapDescLayout = VK_NULL_HANDLE;
// Для отрисовки с использованием текстурного атласа и карты освещения
VkPipelineLayout MainAtlas_LightMap_PipelineLayout = VK_NULL_HANDLE;
// Для отрисовки вокселей
std::shared_ptr<ShaderModule> VoxelShaderVertex, VoxelShaderGeometry, VoxelShaderFragmentOpaque, VoxelShaderFragmentTransparent;
VkPipeline
VoxelOpaquePipeline = VK_NULL_HANDLE, // Альфа канал может быть либо 255, либо 0
VoxelTransparentPipeline = VK_NULL_HANDLE; // Допускается полупрозрачность и смешивание
// Для отрисовки статичных, не анимированных нод
std::shared_ptr<ShaderModule> NodeShaderVertex, NodeShaderGeometry, NodeShaderFragmentOpaque, NodeShaderFragmentTransparent;
VkPipeline
NodeStaticOpaquePipeline = VK_NULL_HANDLE,
NodeStaticTransparentPipeline = VK_NULL_HANDLE;
std::map<TextureId_c, uint16_t> ServerToAtlas;
virtual void free(Vulkan *instance) override;
virtual void init(Vulkan *instance) override;
public:
VulkanRenderSession(VK::Vulkan *vkInst);
WorldPCO PCO;
public:
VulkanRenderSession();
virtual ~VulkanRenderSession();
void setServerSession(IServerSession *serverSession) {
@@ -38,6 +139,13 @@ public:
virtual void onChunksChange(WorldId_c worldId, const std::vector<Pos::GlobalChunk> &changeOrAddList, const std::vector<Pos::GlobalChunk> &remove) override;
virtual void setCameraPos(WorldId_c worldId, Pos::Object pos, glm::quat quat) override;
glm::mat4 calcViewMatrix(glm::quat quat, glm::vec3 camOffset = glm::vec3(0)) {
return glm::translate(glm::mat4(quat), camOffset);
}
void beforeDraw();
void drawWorld(GlobalTime gTime, float dTime, VkCommandBuffer drawCmd);
};
}