Editor now displaying in Vulkan
This commit is contained in:
@@ -411,7 +411,7 @@ namespace Nuake
|
||||
vblayout.Push<float>(3);
|
||||
lineVertexArray.AddBuffer(lineVertexBuffer, vblayout);
|
||||
|
||||
RenderCommand::DrawLines(0, 2);
|
||||
//RenderCommand::DrawLines(0, 2);
|
||||
}
|
||||
|
||||
void Renderer::DrawLine(Vector3 start, Vector3 end, Vector3 color)
|
||||
@@ -424,8 +424,8 @@ namespace Nuake
|
||||
void Renderer::DrawCube(Matrix4 transform)
|
||||
{
|
||||
ZoneScoped;
|
||||
CubeMesh->Bind();
|
||||
RenderCommand::DrawArrays(0, 36);
|
||||
//CubeMesh->Bind();
|
||||
//RenderCommand::DrawArrays(0, 36);
|
||||
}
|
||||
|
||||
|
||||
@@ -438,7 +438,7 @@ namespace Nuake
|
||||
{
|
||||
ZoneScoped;
|
||||
|
||||
QuadMesh->Bind();
|
||||
RenderCommand::DrawArrays(0, 6);
|
||||
//QuadMesh->Bind();
|
||||
//RenderCommand::DrawArrays(0, 6);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,6 +182,8 @@ namespace Nuake
|
||||
/// <param name="framebuffer">Framebuffer to render the scene to. Should be in the right size</param>
|
||||
void SceneRenderer::RenderScene(Scene& scene, FrameBuffer& framebuffer, bool renderUI)
|
||||
{
|
||||
return;
|
||||
|
||||
ZoneScoped;
|
||||
|
||||
// Renders all shadow maps
|
||||
@@ -694,7 +696,7 @@ namespace Nuake
|
||||
shader->Bind();
|
||||
|
||||
RenderCommand::Enable(RendererEnum::FACE_CULL);
|
||||
glCullFace(GL_BACK);
|
||||
//glCullFace(GL_BACK);
|
||||
|
||||
auto meshView = scene.m_Registry.view<TransformComponent, ModelComponent, VisibilityComponent>();
|
||||
auto quakeView = scene.m_Registry.view<TransformComponent, BSPBrushComponent, VisibilityComponent>();
|
||||
|
||||
@@ -153,11 +153,11 @@ namespace Nuake
|
||||
else
|
||||
m_DefaultDisplacement->Bind(9);
|
||||
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, UBO);
|
||||
|
||||
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UBOStructure), &data);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 32, UBO);
|
||||
//glBindBuffer(GL_UNIFORM_BUFFER, UBO);
|
||||
//
|
||||
//glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UBOStructure), &data);
|
||||
//glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
//glBindBufferBase(GL_UNIFORM_BUFFER, 32, UBO);
|
||||
//Renderer::m_Shader->SetUniform("m_Displacement", 9);
|
||||
//Renderer::m_Shader->SetUniform("m_Displacement", 9);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
#include "src/Window.h"
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/imgui_impl_vulkan.h"
|
||||
#include <imgui/imgui_impl_glfw.h>
|
||||
#include "src/Resource/StaticResources.h"
|
||||
|
||||
using namespace Nuake;
|
||||
|
||||
@@ -97,6 +101,8 @@ void VkRenderer::Initialize()
|
||||
|
||||
InitPipeline();
|
||||
|
||||
InitImgui();
|
||||
|
||||
IsInitialized = true;
|
||||
}
|
||||
|
||||
@@ -160,12 +166,17 @@ void VkRenderer::SelectGPU()
|
||||
VkPhysicalDeviceVulkan12Features features12{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES };
|
||||
features12.bufferDeviceAddress = true;
|
||||
features12.descriptorIndexing = true;
|
||||
|
||||
std::vector<const char*> requiredExtensions = { VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME };
|
||||
|
||||
|
||||
vkb::PhysicalDeviceSelector selector{ VkbInstance };
|
||||
vkb::PhysicalDevice physicalDevice = selector
|
||||
.set_minimum_version(1, 3)
|
||||
.set_required_features_13(features)
|
||||
.set_required_features_12(features12)
|
||||
.set_surface(Surface)
|
||||
.add_required_extensions(requiredExtensions)
|
||||
.select()
|
||||
.value();
|
||||
|
||||
@@ -273,6 +284,17 @@ void VkRenderer::InitCommands()
|
||||
|
||||
VK_CALL(vkAllocateCommandBuffers(Device, &cmdAllocInfo, &Frames[i].CommandBuffer));
|
||||
}
|
||||
|
||||
VK_CALL(vkCreateCommandPool(Device, &cmdPoolInfo, nullptr, &ImguiCommandPool));
|
||||
|
||||
// allocate the command buffer for immediate submits
|
||||
VkCommandBufferAllocateInfo cmdAllocInfo = VulkanInit::CommandBufferAllocateInfo(ImguiCommandPool, 1);
|
||||
|
||||
VK_CALL(vkAllocateCommandBuffers(Device, &cmdAllocInfo, &ImguiCommandBuffer));
|
||||
|
||||
MainDeletionQueue.push_function([=]() {
|
||||
vkDestroyCommandPool(Device, ImguiCommandPool, nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
void VkRenderer::InitSync()
|
||||
@@ -291,6 +313,9 @@ void VkRenderer::InitSync()
|
||||
VK_CALL(vkCreateSemaphore(Device, &semaphoreCreateInfo, nullptr, &Frames[i].SwapchainSemaphore));
|
||||
VK_CALL(vkCreateSemaphore(Device, &semaphoreCreateInfo, nullptr, &Frames[i].RenderSemaphore));
|
||||
}
|
||||
|
||||
VK_CALL(vkCreateFence(Device, &fenceCreateInfo, nullptr, &ImguiFence));
|
||||
MainDeletionQueue.push_function([=]() { vkDestroyFence(Device, ImguiFence, nullptr); });
|
||||
}
|
||||
|
||||
void VkRenderer::InitDescriptors()
|
||||
@@ -370,6 +395,171 @@ void VkRenderer::InitBackgroundPipeline()
|
||||
});
|
||||
}
|
||||
|
||||
void VkRenderer::InitImgui()
|
||||
{
|
||||
// 1: create descriptor pool for IMGUI
|
||||
// the size of the pool is very oversize, but it's copied from imgui demo
|
||||
// itself.
|
||||
VkDescriptorPoolSize pool_sizes[] = { { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } };
|
||||
|
||||
VkDescriptorPoolCreateInfo pool_info = {};
|
||||
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
|
||||
pool_info.maxSets = 1000;
|
||||
pool_info.poolSizeCount = (uint32_t)std::size(pool_sizes);
|
||||
pool_info.pPoolSizes = pool_sizes;
|
||||
|
||||
VkDescriptorPool imguiPool;
|
||||
VK_CALL(vkCreateDescriptorPool(Device, &pool_info, nullptr, &imguiPool));
|
||||
|
||||
// 2: initialize imgui library
|
||||
|
||||
// this initializes the core structures of imgui
|
||||
ImGui::CreateContext();
|
||||
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
||||
io.Fonts->AddFontFromMemoryTTF(StaticResources::Resources_Fonts_Poppins_Regular_ttf, StaticResources::Resources_Fonts_Poppins_Regular_ttf_len, 16.0);
|
||||
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||
//io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
|
||||
ImGui::StyleColorsDark();
|
||||
|
||||
ImGuiStyle& s = ImGui::GetStyle();
|
||||
s.WindowMenuButtonPosition = ImGuiDir_None;
|
||||
s.GrabRounding = 2.0f;
|
||||
s.CellPadding = ImVec2(8, 8);
|
||||
s.WindowPadding = ImVec2(4, 4);
|
||||
s.ScrollbarRounding = 9.0f;
|
||||
s.ScrollbarSize = 15.0f;
|
||||
s.GrabMinSize = 32.0f;
|
||||
s.TabRounding = 0;
|
||||
s.WindowRounding = 4.0f;
|
||||
s.ChildRounding = 4.0f;
|
||||
s.FrameRounding = 4.0f;
|
||||
s.GrabRounding = 0;
|
||||
s.FramePadding = ImVec2(8, 4);
|
||||
s.ItemSpacing = ImVec2(8, 4);
|
||||
s.ItemInnerSpacing = ImVec2(4, 4);
|
||||
s.TabRounding = 4.0f;
|
||||
s.WindowBorderSize = 0.0f;
|
||||
s.IndentSpacing = 12.0f;
|
||||
s.ChildBorderSize = 0.0f;
|
||||
s.PopupRounding = 4.0f;
|
||||
s.FrameBorderSize = 0.0f;
|
||||
|
||||
ImVec4* colors = ImGui::GetStyle().Colors;
|
||||
colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
|
||||
colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
|
||||
colors[ImGuiCol_WindowBg] = ImVec4(0.148f, 0.148f, 0.148f, 1.00f);
|
||||
colors[ImGuiCol_ChildBg] = ImVec4(0.15f, 0.15f, 0.15f, 1.00f);
|
||||
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
|
||||
colors[ImGuiCol_Border] = ImVec4(0.11f, 0.11f, 0.11f, 1.00f);
|
||||
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
colors[ImGuiCol_FrameBg] = ImVec4(0.08f, 0.08f, 0.08f, 1.00f);
|
||||
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.09f, 0.09f, 0.09f, 1.00f);
|
||||
colors[ImGuiCol_FrameBgActive] = ImVec4(0.13f, 0.13f, 0.13f, 1.00f);
|
||||
colors[ImGuiCol_TitleBg] = ImVec4(0.08f, 0.08f, 0.08f, 1.00f);
|
||||
colors[ImGuiCol_TitleBgActive] = ImVec4(0.078f, 0.078f, 0.078f, 1.00f);
|
||||
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
|
||||
colors[ImGuiCol_MenuBarBg] = ImVec4(0.08f, 0.08f, 0.08f, 1.00f);
|
||||
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.00f);
|
||||
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.34f, 0.34f, 0.34f, 1.00f);
|
||||
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.29f, 0.29f, 0.29f, 1.00f);
|
||||
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.16f, 0.16f, 0.16f, 1.00f);
|
||||
colors[ImGuiCol_CheckMark] = ImVec4(0.55f, 0.76f, 0.29f, 1.00f);
|
||||
colors[ImGuiCol_SliderGrab] = ImVec4(0.55f, 0.76f, 0.29f, 1.00f);
|
||||
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.55f, 0.76f, 0.29f, 1.00f);
|
||||
colors[ImGuiCol_Button] = ImVec4(0.22f, 0.22f, 0.22f, 1.00f);
|
||||
colors[ImGuiCol_ButtonHovered] = ImVec4(0.19f, 0.19f, 0.19f, 1.00f);
|
||||
colors[ImGuiCol_ButtonActive] = ImVec4(0.16f, 0.16f, 0.16f, 1.00f);
|
||||
colors[ImGuiCol_Header] = ImVec4(0.18f, 0.18f, 0.18f, 1.00f);
|
||||
colors[ImGuiCol_HeaderHovered] = ImVec4(0.16f, 0.16f, 0.16f, 1.00f);
|
||||
colors[ImGuiCol_HeaderActive] = ImVec4(0.12f, 0.12f, 0.12f, 1.00f);
|
||||
colors[ImGuiCol_Separator] = ImVec4(0.08f, 0.08f, 0.08f, 1.00f);
|
||||
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.10f, 0.10f, 0.10f, 0.78f);
|
||||
colors[ImGuiCol_SeparatorActive] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.00f);
|
||||
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.00f);
|
||||
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.00f);
|
||||
colors[ImGuiCol_Tab] = ImVec4(0.08f, 0.08f, 0.08f, 1.00f);
|
||||
colors[ImGuiCol_TabHovered] = ImVec4(0.19f, 0.19f, 0.19f, 1.00f);
|
||||
colors[ImGuiCol_TabActive] = ImVec4(0.15f, 0.15f, 0.15f, 1.00f);
|
||||
colors[ImGuiCol_TabUnfocused] = ImVec4(0.08f, 0.08f, 0.08f, 1.00f);
|
||||
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.15f, 0.15f, 0.15f, 1.00f);
|
||||
colors[ImGuiCol_DockingPreview] = ImVec4(0.55f, 0.76f, 0.29f, 1.00f);
|
||||
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
|
||||
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
|
||||
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
||||
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_Border] = ImVec4(0.078f, 0.078f, 0.078f, 1.00f);
|
||||
colors[ImGuiCol_TableHeaderBg] = ImVec4(0.19f, 0.19f, 0.19f, 1.00f);
|
||||
colors[ImGuiCol_TableBorderStrong] = ImVec4(0.11f, 0.11f, 0.11f, 1.00f);
|
||||
colors[ImGuiCol_TableBorderLight] = ImVec4(0.11f, 0.11f, 0.11f, 1.00f);
|
||||
colors[ImGuiCol_TableRowBg] = ImVec4(0.11f, 0.11f, 0.11f, 1.00f);
|
||||
colors[ImGuiCol_TableRowBgAlt] = ImVec4(0.93f, 0.27f, 0.27f, 0.00f);
|
||||
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.08f, 0.49f, 0.97f, 0.28f);
|
||||
colors[ImGuiCol_DragDropTarget] = ImVec4(0.55f, 0.76f, 0.29f, 1.00f);
|
||||
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
|
||||
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
|
||||
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
|
||||
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
|
||||
}
|
||||
|
||||
auto load_vk_func = [&](const char* fn) {
|
||||
if (auto proc = vkGetDeviceProcAddr(Device, fn)) return proc;
|
||||
return vkGetInstanceProcAddr(Instance, fn);
|
||||
};
|
||||
ImGui_ImplVulkan_LoadFunctions([](const char* fn, void* data) {
|
||||
return (*(decltype(load_vk_func)*)data)(fn);
|
||||
}, &load_vk_func);
|
||||
|
||||
// this initializes imgui for SDL
|
||||
ImGui_ImplGlfw_InitForVulkan(Window::Get()->GetHandle(), true);
|
||||
|
||||
// this initializes imgui for Vulkan
|
||||
ImGui_ImplVulkan_InitInfo init_info = {};
|
||||
init_info.Instance = Instance;
|
||||
init_info.PhysicalDevice = GPU;
|
||||
init_info.Device = Device;
|
||||
init_info.Queue = GPUQueue;
|
||||
init_info.DescriptorPool = imguiPool;
|
||||
init_info.MinImageCount = 3;
|
||||
init_info.ImageCount = 3;
|
||||
init_info.UseDynamicRendering = true;
|
||||
|
||||
//dynamic rendering parameters for imgui to use
|
||||
init_info.PipelineRenderingCreateInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO };
|
||||
init_info.PipelineRenderingCreateInfo.colorAttachmentCount = 1;
|
||||
init_info.PipelineRenderingCreateInfo.pColorAttachmentFormats = &SwapchainImageFormat;
|
||||
|
||||
|
||||
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
ImGui_ImplVulkan_Init(&init_info);
|
||||
|
||||
ImGui_ImplVulkan_CreateFontsTexture();
|
||||
|
||||
// add the destroy the imgui created structures
|
||||
MainDeletionQueue.push_function([=]() {
|
||||
ImGui_ImplVulkan_Shutdown();
|
||||
vkDestroyDescriptorPool(Device, imguiPool, nullptr);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
void VkRenderer::Draw()
|
||||
{
|
||||
VK_CALL(vkWaitForFences(Device, 1, &GetCurrentFrame().RenderFence, true, 1000000000));
|
||||
@@ -420,6 +610,15 @@ void VkRenderer::Draw()
|
||||
// Execute a copy from the rendering image into the swapchain
|
||||
VulkanUtil::CopyImageToImage(cmd, DrawImage.image, SwapchainImages[swapchainImageIndex], DrawExtent, SwapchainExtent);
|
||||
|
||||
// set swapchain image layout to Attachment Optimal so we can draw it
|
||||
VulkanUtil::TransitionImage(cmd, SwapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
|
||||
//draw imgui into the swapchain image
|
||||
DrawImgui(cmd, SwapchainImageViews[swapchainImageIndex]);
|
||||
|
||||
// set swapchain image layout to Present so we can draw it
|
||||
VulkanUtil::TransitionImage(cmd, SwapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
|
||||
// Transition the swapchain image to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR for presentation
|
||||
VulkanUtil::TransitionImage(cmd, SwapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
}
|
||||
@@ -472,6 +671,43 @@ void VkRenderer::DrawBackground(VkCommandBuffer cmd)
|
||||
vkCmdDispatch(cmd, std::ceil(DrawExtent.width / 16.0), std::ceil(DrawExtent.height / 16.0), 1);
|
||||
}
|
||||
|
||||
void VkRenderer::DrawImgui(VkCommandBuffer cmd, VkImageView targetImageView)
|
||||
{
|
||||
VkRenderingAttachmentInfo colorAttachment = VulkanInit::AttachmentInfo(targetImageView, nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
VkRenderingInfo renderInfo = VulkanInit::RenderingInfo(SwapchainExtent, &colorAttachment, nullptr);
|
||||
|
||||
vkCmdBeginRendering(cmd, &renderInfo);
|
||||
|
||||
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd);
|
||||
|
||||
vkCmdEndRendering(cmd);
|
||||
}
|
||||
|
||||
void VkRenderer::ImmediateSubmit(std::function<void(VkCommandBuffer cmd)>&& function)
|
||||
{
|
||||
VK_CALL(vkResetFences(Device, 1, &ImguiFence));
|
||||
VK_CALL(vkResetCommandBuffer(ImguiCommandBuffer, 0));
|
||||
|
||||
VkCommandBuffer cmd = ImguiCommandBuffer;
|
||||
|
||||
VkCommandBufferBeginInfo cmdBeginInfo = VulkanInit::CommandBufferBeginInfo(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
|
||||
|
||||
VK_CALL(vkBeginCommandBuffer(cmd, &cmdBeginInfo));
|
||||
|
||||
function(cmd);
|
||||
|
||||
VK_CALL(vkEndCommandBuffer(cmd));
|
||||
|
||||
VkCommandBufferSubmitInfo cmdinfo = VulkanInit::CommandBufferSubmitInfo(cmd);
|
||||
VkSubmitInfo2 submit = VulkanInit::SubmitInfo(&cmdinfo, nullptr, nullptr);
|
||||
|
||||
// submit command buffer to the queue and execute it.
|
||||
// _renderFence will now block until the graphic commands finish execution
|
||||
VK_CALL(vkQueueSubmit2(GPUQueue, 1, &submit, ImguiFence));
|
||||
|
||||
VK_CALL(vkWaitForFences(Device, 1, &ImguiFence, true, 9999999999));
|
||||
}
|
||||
|
||||
|
||||
// Since well only be using those settings, this is just a shorthand.
|
||||
// Well only be using VK_COMMAND_BUFFER_LEVEL_PRIMARY
|
||||
@@ -623,6 +859,41 @@ VkImageViewCreateInfo VulkanInit::ImageviewCreateInfo(VkFormat format, VkImage i
|
||||
return info;
|
||||
}
|
||||
|
||||
VkRenderingAttachmentInfo VulkanInit::AttachmentInfo(VkImageView view, VkClearValue* clear, VkImageLayout layout)
|
||||
{
|
||||
VkRenderingAttachmentInfo colorAttachment{};
|
||||
colorAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
|
||||
colorAttachment.pNext = nullptr;
|
||||
|
||||
colorAttachment.imageView = view;
|
||||
colorAttachment.imageLayout = layout;
|
||||
colorAttachment.loadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
if (clear)
|
||||
{
|
||||
colorAttachment.clearValue = *clear;
|
||||
}
|
||||
|
||||
return colorAttachment;
|
||||
}
|
||||
|
||||
VkRenderingInfo VulkanInit::RenderingInfo(VkExtent2D renderExtent, VkRenderingAttachmentInfo* colorAttachment,
|
||||
VkRenderingAttachmentInfo* depthAttachment)
|
||||
{
|
||||
VkRenderingInfo renderInfo{};
|
||||
renderInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
|
||||
renderInfo.pNext = nullptr;
|
||||
|
||||
renderInfo.renderArea = VkRect2D{ VkOffset2D { 0, 0 }, renderExtent };
|
||||
renderInfo.layerCount = 1;
|
||||
renderInfo.colorAttachmentCount = 1;
|
||||
renderInfo.pColorAttachments = colorAttachment;
|
||||
renderInfo.pDepthAttachment = depthAttachment;
|
||||
renderInfo.pStencilAttachment = nullptr;
|
||||
|
||||
return renderInfo;
|
||||
}
|
||||
|
||||
// This is a helper to transtion images between readable, writable layouts.
|
||||
void VulkanUtil::TransitionImage(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageLayout newLayout)
|
||||
{
|
||||
|
||||
@@ -161,6 +161,11 @@ namespace Nuake
|
||||
|
||||
Ref<VulkanShader> BackgroundShader;
|
||||
|
||||
// Imgui
|
||||
VkFence ImguiFence;
|
||||
VkCommandBuffer ImguiCommandBuffer;
|
||||
VkCommandPool ImguiCommandPool;
|
||||
|
||||
public:
|
||||
static VkRenderer& Get()
|
||||
{
|
||||
@@ -187,17 +192,19 @@ namespace Nuake
|
||||
void CreateSwapchain(const Vector2& size);
|
||||
void DestroySwapchain();
|
||||
|
||||
|
||||
|
||||
void InitCommands();
|
||||
void InitSync();
|
||||
void InitDescriptors();
|
||||
void UpdateDescriptorSets();
|
||||
void InitPipeline();
|
||||
void InitBackgroundPipeline();
|
||||
void InitImgui();
|
||||
|
||||
void Draw();
|
||||
void DrawBackground(VkCommandBuffer cmd);
|
||||
void DrawImgui(VkCommandBuffer cmd, VkImageView targetImageView);
|
||||
|
||||
void ImmediateSubmit(std::function<void(VkCommandBuffer cmd)>&& function);
|
||||
};
|
||||
|
||||
|
||||
@@ -220,6 +227,9 @@ namespace Nuake
|
||||
|
||||
static VkImageCreateInfo ImageCreateInfo(VkFormat format, VkImageUsageFlags usageFlags, VkExtent3D extent);
|
||||
static VkImageViewCreateInfo ImageviewCreateInfo(VkFormat format, VkImage image, VkImageAspectFlags aspectFlags);
|
||||
|
||||
static VkRenderingAttachmentInfo AttachmentInfo(VkImageView view, VkClearValue* clear, VkImageLayout layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
static VkRenderingInfo RenderingInfo(VkExtent2D renderExtent, VkRenderingAttachmentInfo * colorAttachment, VkRenderingAttachmentInfo * depthAttachment);
|
||||
};
|
||||
|
||||
class VulkanUtil
|
||||
|
||||
@@ -53,7 +53,7 @@ void UIResource::Draw()
|
||||
|
||||
if (canvas != nullptr)
|
||||
{
|
||||
canvas->Draw();
|
||||
//canvas->Draw();
|
||||
}
|
||||
}
|
||||
framebuffer->Unbind();
|
||||
|
||||
@@ -427,7 +427,7 @@ namespace Nuake
|
||||
void Scene::Draw(FrameBuffer& framebuffer, const Matrix4& projection, const Matrix4& view)
|
||||
{
|
||||
m_SceneRenderer->BeginRenderScene(m_EditorCamera->GetPerspective(), m_EditorCamera->GetTransform(), m_EditorCamera->Translation);
|
||||
m_SceneRenderer->RenderScene(*this, framebuffer);
|
||||
//m_SceneRenderer->RenderScene(*this, framebuffer);
|
||||
}
|
||||
|
||||
std::vector<Entity> Scene::GetAllEntities()
|
||||
|
||||
1585
Nuake/src/Vendors/imgui/imgui_impl_vulkan.cpp
Normal file
1585
Nuake/src/Vendors/imgui/imgui_impl_vulkan.cpp
Normal file
File diff suppressed because it is too large
Load Diff
188
Nuake/src/Vendors/imgui/imgui_impl_vulkan.h
Normal file
188
Nuake/src/Vendors/imgui/imgui_impl_vulkan.h
Normal file
@@ -0,0 +1,188 @@
|
||||
// dear imgui: Renderer Backend for Vulkan
|
||||
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
|
||||
|
||||
// Implemented features:
|
||||
// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions.
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
||||
|
||||
// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'.
|
||||
// See imgui_impl_vulkan.cpp file for details.
|
||||
|
||||
// The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification.
|
||||
// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// Learn about Dear ImGui:
|
||||
// - FAQ https://dearimgui.com/faq
|
||||
// - Getting Started https://dearimgui.com/getting-started
|
||||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - Introduction, links and more at the top of imgui.cpp
|
||||
|
||||
// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app.
|
||||
// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h.
|
||||
// You will use those if you want to use this rendering backend in your engine/app.
|
||||
// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by
|
||||
// the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code.
|
||||
// Read comments in imgui_impl_vulkan.h.
|
||||
|
||||
#pragma once
|
||||
#ifndef IMGUI_DISABLE
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
|
||||
// [Configuration] in order to use a custom Vulkan function loader:
|
||||
// (1) You'll need to disable default Vulkan function prototypes.
|
||||
// We provide a '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' convenience configuration flag.
|
||||
// In order to make sure this is visible from the imgui_impl_vulkan.cpp compilation unit:
|
||||
// - Add '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' in your imconfig.h file
|
||||
// - Or as a compilation flag in your build system
|
||||
// - Or uncomment here (not recommended because you'd be modifying imgui sources!)
|
||||
// - Do not simply add it in a .cpp file!
|
||||
// (2) Call ImGui_ImplVulkan_LoadFunctions() before ImGui_ImplVulkan_Init() with your custom function.
|
||||
// If you have no idea what this is, leave it alone!
|
||||
//#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES
|
||||
|
||||
// Vulkan includes
|
||||
#if defined(IMGUI_IMPL_VULKAN_NO_PROTOTYPES) && !defined(VK_NO_PROTOTYPES)
|
||||
#define VK_NO_PROTOTYPES
|
||||
#endif
|
||||
#if defined(VK_USE_PLATFORM_WIN32_KHR) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#include <vulkan/vulkan.h>
|
||||
#else
|
||||
#include <vulkan/vulkan.h>
|
||||
#endif
|
||||
#if defined(VK_VERSION_1_3) || defined(VK_KHR_dynamic_rendering)
|
||||
#define IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
|
||||
#endif
|
||||
|
||||
// Initialization data, for ImGui_ImplVulkan_Init()
|
||||
// - VkDescriptorPool should be created with VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
|
||||
// and must contain a pool size large enough to hold an ImGui VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER descriptor.
|
||||
// - When using dynamic rendering, set UseDynamicRendering=true and fill PipelineRenderingCreateInfo structure.
|
||||
// [Please zero-clear before use!]
|
||||
struct ImGui_ImplVulkan_InitInfo
|
||||
{
|
||||
VkInstance Instance;
|
||||
VkPhysicalDevice PhysicalDevice;
|
||||
VkDevice Device;
|
||||
uint32_t QueueFamily;
|
||||
VkQueue Queue;
|
||||
VkDescriptorPool DescriptorPool; // See requirements in note above
|
||||
VkRenderPass RenderPass; // Ignored if using dynamic rendering
|
||||
uint32_t MinImageCount; // >= 2
|
||||
uint32_t ImageCount; // >= MinImageCount
|
||||
VkSampleCountFlagBits MSAASamples; // 0 defaults to VK_SAMPLE_COUNT_1_BIT
|
||||
|
||||
// (Optional)
|
||||
VkPipelineCache PipelineCache;
|
||||
uint32_t Subpass;
|
||||
|
||||
// (Optional) Dynamic Rendering
|
||||
// Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3.
|
||||
bool UseDynamicRendering;
|
||||
#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
|
||||
VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo;
|
||||
#endif
|
||||
|
||||
// (Optional) Allocation, Debugging
|
||||
const VkAllocationCallbacks* Allocator;
|
||||
void (*CheckVkResultFn)(VkResult err);
|
||||
VkDeviceSize MinAllocationSize; // Minimum allocation size. Set to 1024*1024 to satisfy zealous best practices validation layer and waste a little memory.
|
||||
};
|
||||
|
||||
// Called by user code
|
||||
IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info);
|
||||
IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame();
|
||||
IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE);
|
||||
IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture();
|
||||
IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontsTexture();
|
||||
IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated)
|
||||
|
||||
// Register a texture (VkDescriptorSet == ImTextureID)
|
||||
// FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem
|
||||
// Please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions.
|
||||
IMGUI_IMPL_API VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout);
|
||||
IMGUI_IMPL_API void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set);
|
||||
|
||||
// Optional: load Vulkan functions with a custom function loader
|
||||
// This is only useful with IMGUI_IMPL_VULKAN_NO_PROTOTYPES / VK_NO_PROTOTYPES
|
||||
IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = nullptr);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Internal / Miscellaneous Vulkan Helpers
|
||||
// (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own engine/app.)
|
||||
//-------------------------------------------------------------------------
|
||||
// You probably do NOT need to use or care about those functions.
|
||||
// Those functions only exist because:
|
||||
// 1) they facilitate the readability and maintenance of the multiple main.cpp examples files.
|
||||
// 2) the multi-viewport / platform window implementation needs them internally.
|
||||
// Generally we avoid exposing any kind of superfluous high-level helpers in the bindings,
|
||||
// but it is too much code to duplicate everywhere so we exceptionally expose them.
|
||||
//
|
||||
// Your engine/app will likely _already_ have code to setup all that stuff (swap chain, render pass, frame buffers, etc.).
|
||||
// You may read this code to learn about Vulkan, but it is recommended you use you own custom tailored code to do equivalent work.
|
||||
// (The ImGui_ImplVulkanH_XXX functions do not interact with any of the state used by the regular ImGui_ImplVulkan_XXX functions)
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
struct ImGui_ImplVulkanH_Frame;
|
||||
struct ImGui_ImplVulkanH_Window;
|
||||
|
||||
// Helpers
|
||||
IMGUI_IMPL_API void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wnd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count);
|
||||
IMGUI_IMPL_API void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wnd, const VkAllocationCallbacks* allocator);
|
||||
IMGUI_IMPL_API VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space);
|
||||
IMGUI_IMPL_API VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count);
|
||||
IMGUI_IMPL_API int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode);
|
||||
|
||||
// Helper structure to hold the data needed by one rendering frame
|
||||
// (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.)
|
||||
// [Please zero-clear before use!]
|
||||
struct ImGui_ImplVulkanH_Frame
|
||||
{
|
||||
VkCommandPool CommandPool;
|
||||
VkCommandBuffer CommandBuffer;
|
||||
VkFence Fence;
|
||||
VkImage Backbuffer;
|
||||
VkImageView BackbufferView;
|
||||
VkFramebuffer Framebuffer;
|
||||
};
|
||||
|
||||
struct ImGui_ImplVulkanH_FrameSemaphores
|
||||
{
|
||||
VkSemaphore ImageAcquiredSemaphore;
|
||||
VkSemaphore RenderCompleteSemaphore;
|
||||
};
|
||||
|
||||
// Helper structure to hold the data needed by one rendering context into one OS window
|
||||
// (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.)
|
||||
struct ImGui_ImplVulkanH_Window
|
||||
{
|
||||
int Width;
|
||||
int Height;
|
||||
VkSwapchainKHR Swapchain;
|
||||
VkSurfaceKHR Surface;
|
||||
VkSurfaceFormatKHR SurfaceFormat;
|
||||
VkPresentModeKHR PresentMode;
|
||||
VkRenderPass RenderPass;
|
||||
VkPipeline Pipeline; // The window pipeline may uses a different VkRenderPass than the one passed in ImGui_ImplVulkan_InitInfo
|
||||
bool UseDynamicRendering;
|
||||
bool ClearEnable;
|
||||
VkClearValue ClearValue;
|
||||
uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount)
|
||||
uint32_t ImageCount; // Number of simultaneous in-flight frames (returned by vkGetSwapchainImagesKHR, usually derived from min_image_count)
|
||||
uint32_t SemaphoreCount; // Number of simultaneous in-flight frames + 1, to be able to use it in vkAcquireNextImageKHR
|
||||
uint32_t SemaphoreIndex; // Current set of swapchain wait semaphores we're using (needs to be distinct from per frame data)
|
||||
ImGui_ImplVulkanH_Frame* Frames;
|
||||
ImGui_ImplVulkanH_FrameSemaphores* FrameSemaphores;
|
||||
|
||||
ImGui_ImplVulkanH_Window()
|
||||
{
|
||||
memset((void*)this, 0, sizeof(*this));
|
||||
PresentMode = (VkPresentModeKHR)~0; // Ensure we get an error if user doesn't set this.
|
||||
ClearEnable = true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
6
Nuake/src/Vendors/imgui/vulkan/generate_spv.sh
Normal file
6
Nuake/src/Vendors/imgui/vulkan/generate_spv.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
## -V: create SPIR-V binary
|
||||
## -x: save binary output as text-based 32-bit hexadecimal numbers
|
||||
## -o: output file
|
||||
glslangValidator -V -x -o glsl_shader.frag.u32 glsl_shader.frag
|
||||
glslangValidator -V -x -o glsl_shader.vert.u32 glsl_shader.vert
|
||||
14
Nuake/src/Vendors/imgui/vulkan/glsl_shader.frag
Normal file
14
Nuake/src/Vendors/imgui/vulkan/glsl_shader.frag
Normal file
@@ -0,0 +1,14 @@
|
||||
#version 450 core
|
||||
layout(location = 0) out vec4 fColor;
|
||||
|
||||
layout(set=0, binding=0) uniform sampler2D sTexture;
|
||||
|
||||
layout(location = 0) in struct {
|
||||
vec4 Color;
|
||||
vec2 UV;
|
||||
} In;
|
||||
|
||||
void main()
|
||||
{
|
||||
fColor = In.Color * texture(sTexture, In.UV.st);
|
||||
}
|
||||
25
Nuake/src/Vendors/imgui/vulkan/glsl_shader.vert
Normal file
25
Nuake/src/Vendors/imgui/vulkan/glsl_shader.vert
Normal file
@@ -0,0 +1,25 @@
|
||||
#version 450 core
|
||||
layout(location = 0) in vec2 aPos;
|
||||
layout(location = 1) in vec2 aUV;
|
||||
layout(location = 2) in vec4 aColor;
|
||||
|
||||
layout(push_constant) uniform uPushConstant {
|
||||
vec2 uScale;
|
||||
vec2 uTranslate;
|
||||
} pc;
|
||||
|
||||
out gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
layout(location = 0) out struct {
|
||||
vec4 Color;
|
||||
vec2 UV;
|
||||
} Out;
|
||||
|
||||
void main()
|
||||
{
|
||||
Out.Color = aColor;
|
||||
Out.UV = aUV;
|
||||
gl_Position = vec4(aPos * pc.uScale + pc.uTranslate, 0, 1);
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "src/Rendering/Renderer.h"
|
||||
#include "src/Rendering/Buffers/Framebuffer.h"
|
||||
#include "src/Scene/Scene.h"
|
||||
|
||||
#include "src/Rendering/Vulkan/VulkanRenderer.h"
|
||||
#include "src/Resource/StaticResources.h"
|
||||
|
||||
#include <glad/glad.h>
|
||||
@@ -148,6 +148,7 @@ int Window::Init()
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
#endif // NK_VK
|
||||
const Vector2 defaultResolution = Vector2(1, 1);
|
||||
this->framebuffer = CreateRef<FrameBuffer>(true, defaultResolution);
|
||||
|
||||
@@ -158,7 +159,6 @@ int Window::Init()
|
||||
this->framebuffer->SetTexture(outputTexture);
|
||||
this->framebuffer->SetTexture(CreateRef<Texture>(defaultResolution, GL_RED_INTEGER, GL_R32I, GL_INT), GL_COLOR_ATTACHMENT1); // Entity ID
|
||||
|
||||
#endif // NK_VK
|
||||
|
||||
//glEnable(GL_CULL_FACE);
|
||||
|
||||
@@ -232,7 +232,9 @@ void Window::EndDraw()
|
||||
ImGui::Render();
|
||||
}
|
||||
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
VkRenderer::Get().Draw();
|
||||
|
||||
//ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
||||
{
|
||||
@@ -536,4 +538,5 @@ void Window::InitImgui()
|
||||
|
||||
ImGui_ImplGlfw_InitForOpenGL(this->window, true);
|
||||
ImGui_ImplOpenGL3_Init("#version 330");
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user