pipelines and renderpasses
This commit is contained in:
@@ -57,7 +57,7 @@ BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 0
|
||||
ColumnLimit: 90
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
QualifierAlignment: Leave
|
||||
CompactNamespaces: false
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
#include "pipeline.hpp"
|
||||
|
||||
#include "device.hpp"
|
||||
#include "preview_renderer/shader.hpp"
|
||||
#include "renderpass.hpp"
|
||||
#include "swapchain.hpp"
|
||||
|
||||
#include "yolo/yolo.hpp"
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
namespace inferno::graphics {
|
||||
|
||||
Pipeline* pipeline_create(GraphicsDevice* device)
|
||||
@@ -12,12 +17,146 @@ Pipeline* pipeline_create(GraphicsDevice* device)
|
||||
pipeline->Device = device;
|
||||
pipeline->Swap = swapchain_create(device, device->SurfaceSize);
|
||||
|
||||
std::vector<VkDynamicState> dynamicStates = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR,
|
||||
};
|
||||
|
||||
pipeline->DynamicStateCreateInfo.sType
|
||||
= VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||
pipeline->DynamicStateCreateInfo.dynamicStateCount = dynamicStates.size();
|
||||
pipeline->DynamicStateCreateInfo.pDynamicStates = dynamicStates.data();
|
||||
|
||||
pipeline->VertexInputInfo.sType
|
||||
= VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||
pipeline->VertexInputInfo.vertexBindingDescriptionCount = 0;
|
||||
pipeline->VertexInputInfo.pVertexBindingDescriptions = nullptr; // Optional
|
||||
pipeline->VertexInputInfo.vertexAttributeDescriptionCount = 0;
|
||||
pipeline->VertexInputInfo.pVertexAttributeDescriptions
|
||||
= nullptr; // pipeline->Vptional
|
||||
|
||||
pipeline->InputAssembly.sType
|
||||
= VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||
pipeline->InputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
pipeline->InputAssembly.primitiveRestartEnable = VK_FALSE;
|
||||
|
||||
// NOTE: This is where the viewport and scissor are set
|
||||
// In reality, these should be dynamic, so they can be changed
|
||||
|
||||
// VkViewport viewport = {};
|
||||
// viewport.x = 0.f;
|
||||
// viewport.y = 0.f;
|
||||
// viewport.width = (float)pipeline->Swap->Extent.width;
|
||||
// viewport.height = (float)pipeline->Swap->Extent.height;
|
||||
// viewport.minDepth = 0.f
|
||||
// viewport.maxDepth = 1.f;
|
||||
//
|
||||
// VkRect2D scissor = {};
|
||||
// scissor.offset = { 0, 0 };
|
||||
// scissor.extent = pipeline->Swap->Extent;
|
||||
|
||||
pipeline->ViewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
pipeline->ViewportState.viewportCount = 1;
|
||||
pipeline->ViewportState.scissorCount = 1;
|
||||
|
||||
pipeline->Rasterizer.sType
|
||||
= VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||
pipeline->Rasterizer.depthClampEnable = VK_FALSE; // NOTE: This is for shadow mapping
|
||||
pipeline->Rasterizer.rasterizerDiscardEnable = VK_FALSE;
|
||||
pipeline->Rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
|
||||
pipeline->Rasterizer.lineWidth = 1.f;
|
||||
pipeline->Rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
|
||||
pipeline->Rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
|
||||
pipeline->Rasterizer.depthBiasEnable = VK_FALSE;
|
||||
pipeline->Rasterizer.depthBiasConstantFactor = 0.f; // Optional
|
||||
pipeline->Rasterizer.depthBiasClamp = 0.f; // Optional
|
||||
pipeline->Rasterizer.depthBiasSlopeFactor = 0.f; // Optional
|
||||
|
||||
pipeline->Multisampling.sType
|
||||
= VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
pipeline->Multisampling.sampleShadingEnable = VK_FALSE;
|
||||
pipeline->Multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
pipeline->Multisampling.minSampleShading = 1.0f; // Optional
|
||||
pipeline->Multisampling.pSampleMask = nullptr; // Optional
|
||||
pipeline->Multisampling.alphaToCoverageEnable = VK_FALSE; // Optional
|
||||
|
||||
pipeline->ColorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT
|
||||
| VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||
// Alpha pipeline->Blending basically
|
||||
pipeline->ColorBlendAttachment.blendEnable = VK_TRUE;
|
||||
pipeline->ColorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
pipeline->ColorBlendAttachment.dstColorBlendFactor
|
||||
= VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
pipeline->ColorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
pipeline->ColorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
pipeline->ColorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
pipeline->ColorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
|
||||
pipeline->ColorBlending.sType
|
||||
= VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
pipeline->ColorBlending.logicOpEnable = VK_FALSE;
|
||||
pipeline->ColorBlending.logicOp = VK_LOGIC_OP_COPY; // Optional
|
||||
pipeline->ColorBlending.attachmentCount = 1;
|
||||
pipeline->ColorBlending.pAttachments = &pipeline->ColorBlendAttachment;
|
||||
pipeline->ColorBlending.blendConstants[0] = 0.0f; // Optional
|
||||
pipeline->ColorBlending.blendConstants[1] = 0.0f; // Optional
|
||||
pipeline->ColorBlending.blendConstants[2] = 0.0f; // Optional
|
||||
pipeline->ColorBlending.blendConstants[3] = 0.0f; // pipeline->Optional
|
||||
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutInfo {};
|
||||
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
pipelineLayoutInfo.setLayoutCount = 0; // Optional
|
||||
pipelineLayoutInfo.pSetLayouts = nullptr; // Optional
|
||||
pipelineLayoutInfo.pushConstantRangeCount = 0; // Optional
|
||||
pipelineLayoutInfo.pPushConstantRanges = nullptr; // Optional
|
||||
|
||||
if (vkCreatePipelineLayout(
|
||||
device->VulkanDevice, &pipelineLayoutInfo, nullptr, &pipeline->Layout)
|
||||
!= VK_SUCCESS) {
|
||||
yolo::error("failed to create pipeline layout!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
void pipeline_cleanup(Pipeline* pipeline)
|
||||
{
|
||||
vkDestroyPipeline(
|
||||
pipeline->Device->VulkanDevice, pipeline->GraphicsPipeline, nullptr);
|
||||
vkDestroyPipelineLayout(pipeline->Device->VulkanDevice, pipeline->Layout, nullptr);
|
||||
delete pipeline;
|
||||
}
|
||||
|
||||
void pipeline_configure_to_renderpass(
|
||||
Pipeline* pipeline, Shader* shader, RenderPass* renderpass)
|
||||
{
|
||||
VkGraphicsPipelineCreateInfo pipelineInfo = {};
|
||||
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
pipelineInfo.stageCount = 2;
|
||||
pipelineInfo.pStages = shader->ShaderStages;
|
||||
|
||||
pipelineInfo.pVertexInputState = &pipeline->VertexInputInfo;
|
||||
pipelineInfo.pInputAssemblyState = &pipeline->InputAssembly;
|
||||
pipelineInfo.pViewportState = &pipeline->ViewportState;
|
||||
pipelineInfo.pRasterizationState = &pipeline->Rasterizer;
|
||||
pipelineInfo.pMultisampleState = &pipeline->Multisampling;
|
||||
pipelineInfo.pDepthStencilState = nullptr; // Optional
|
||||
pipelineInfo.pColorBlendState = &pipeline->ColorBlending;
|
||||
pipelineInfo.pDynamicState = &pipeline->DynamicStateCreateInfo;
|
||||
|
||||
pipelineInfo.layout = pipeline->Layout;
|
||||
pipelineInfo.renderPass = renderpass->RenderPass;
|
||||
pipelineInfo.subpass = 0;
|
||||
|
||||
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // Optional
|
||||
pipelineInfo.basePipelineIndex = -1; // Optional
|
||||
|
||||
if (vkCreateGraphicsPipelines(pipeline->Device->VulkanDevice, VK_NULL_HANDLE, 1,
|
||||
&pipelineInfo, nullptr, &pipeline->GraphicsPipeline)
|
||||
!= VK_SUCCESS) {
|
||||
yolo::error("failed to create graphics pipeline!");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace inferno::graphics
|
||||
|
||||
@@ -1,18 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "graphics.hpp"
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
namespace inferno::graphics {
|
||||
|
||||
struct GraphicsDevice;
|
||||
struct RenderPass;
|
||||
struct SwapChain;
|
||||
struct Shader;
|
||||
|
||||
typedef struct Pipeline {
|
||||
GraphicsDevice* Device;
|
||||
SwapChain* Swap;
|
||||
|
||||
VkPipeline GraphicsPipeline;
|
||||
VkPipelineLayout Layout;
|
||||
|
||||
VkPipelineDynamicStateCreateInfo DynamicStateCreateInfo;
|
||||
VkPipelineVertexInputStateCreateInfo VertexInputInfo;
|
||||
VkPipelineInputAssemblyStateCreateInfo InputAssembly;
|
||||
VkPipelineViewportStateCreateInfo ViewportState;
|
||||
VkPipelineRasterizationStateCreateInfo Rasterizer;
|
||||
VkPipelineMultisampleStateCreateInfo Multisampling;
|
||||
VkPipelineColorBlendAttachmentState ColorBlendAttachment;
|
||||
VkPipelineColorBlendStateCreateInfo ColorBlending;
|
||||
} Pipeline;
|
||||
|
||||
Pipeline* pipeline_create(GraphicsDevice* device);
|
||||
void pipeline_cleanup(Pipeline* pipeline);
|
||||
|
||||
void pipeline_configure_to_renderpass(Pipeline* pipeline, Shader* shader, RenderPass* renderpass);
|
||||
|
||||
}
|
||||
|
||||
66
src/graphics/renderpass.cpp
Normal file
66
src/graphics/renderpass.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "renderpass.hpp"
|
||||
|
||||
#include "device.hpp"
|
||||
#include "pipeline.hpp"
|
||||
#include "swapchain.hpp"
|
||||
|
||||
#include "preview_renderer/shader.hpp"
|
||||
|
||||
#include "yolo/yolo.hpp"
|
||||
|
||||
namespace inferno::graphics {
|
||||
|
||||
RenderPass* renderpass_create(GraphicsDevice* device)
|
||||
{
|
||||
RenderPass* renderpass = new RenderPass;
|
||||
renderpass->Device = device;
|
||||
renderpass->RenderPipeline = pipeline_create(device);
|
||||
|
||||
VkAttachmentDescription colorAttachment = {};
|
||||
colorAttachment.format = renderpass->RenderPipeline->Swap->ImageFormat;
|
||||
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
|
||||
VkAttachmentReference colorAttachmentRef {};
|
||||
colorAttachmentRef.attachment = 0;
|
||||
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkSubpassDescription subpass {};
|
||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpass.colorAttachmentCount = 1;
|
||||
subpass.pColorAttachments = &colorAttachmentRef; // index = gl_colour in glsl
|
||||
|
||||
VkRenderPassCreateInfo renderPassInfo {};
|
||||
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
renderPassInfo.attachmentCount = 1;
|
||||
renderPassInfo.pAttachments = &colorAttachment;
|
||||
renderPassInfo.subpassCount = 1;
|
||||
renderPassInfo.pSubpasses = &subpass;
|
||||
|
||||
if (vkCreateRenderPass(
|
||||
device->VulkanDevice, &renderPassInfo, nullptr, &renderpass->RenderPass)
|
||||
!= VK_SUCCESS) {
|
||||
yolo::error("failed to create render pass!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Shader* shader = shader_create(device->VulkanDevice);
|
||||
shader_load(shader, "res/shaders/vulkan_test");
|
||||
|
||||
pipeline_configure_to_renderpass(renderpass->RenderPipeline, shader, renderpass);
|
||||
return renderpass;
|
||||
}
|
||||
|
||||
void rendepass_cleanup(RenderPass* renderpass)
|
||||
{
|
||||
pipeline_cleanup(renderpass->RenderPipeline);
|
||||
vkDestroyRenderPass(renderpass->Device->VulkanDevice, renderpass->RenderPass, nullptr);
|
||||
delete renderpass;
|
||||
}
|
||||
|
||||
}
|
||||
21
src/graphics/renderpass.hpp
Normal file
21
src/graphics/renderpass.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "graphics.hpp"
|
||||
|
||||
namespace inferno::graphics {
|
||||
|
||||
struct GraphicsDevice;
|
||||
struct Pipeline;
|
||||
|
||||
typedef struct RenderPass {
|
||||
GraphicsDevice* Device;
|
||||
Pipeline* RenderPipeline;
|
||||
|
||||
VkRenderPass RenderPass;
|
||||
VkFramebuffer Framebuffer;
|
||||
} RenderPass;
|
||||
|
||||
RenderPass* renderpass_create(GraphicsDevice* device);
|
||||
void renderpass_cleanup(RenderPass* renderpass);
|
||||
|
||||
}
|
||||
@@ -12,36 +12,42 @@ struct SwapChainSupportDetails {
|
||||
std::vector<VkPresentModeKHR> PresentModes;
|
||||
};
|
||||
|
||||
SwapChainSupportDetails device_get_swapchain_support(GraphicsDevice* g, VkPhysicalDevice device)
|
||||
SwapChainSupportDetails device_get_swapchain_support(
|
||||
GraphicsDevice* g, VkPhysicalDevice device)
|
||||
{
|
||||
SwapChainSupportDetails details;
|
||||
|
||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, g->VulkanSurface, &details.Capabilities);
|
||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
|
||||
device, g->VulkanSurface, &details.Capabilities);
|
||||
|
||||
uint32_t formatCount;
|
||||
vkGetPhysicalDeviceSurfaceFormatsKHR(device, g->VulkanSurface, &formatCount, nullptr);
|
||||
|
||||
if (formatCount != 0) {
|
||||
details.Formats.resize(formatCount);
|
||||
vkGetPhysicalDeviceSurfaceFormatsKHR(device, g->VulkanSurface, &formatCount, details.Formats.data());
|
||||
vkGetPhysicalDeviceSurfaceFormatsKHR(
|
||||
device, g->VulkanSurface, &formatCount, details.Formats.data());
|
||||
}
|
||||
|
||||
uint32_t presentModeCount;
|
||||
vkGetPhysicalDeviceSurfacePresentModesKHR(device, g->VulkanSurface, &presentModeCount, nullptr);
|
||||
vkGetPhysicalDeviceSurfacePresentModesKHR(
|
||||
device, g->VulkanSurface, &presentModeCount, nullptr);
|
||||
|
||||
if (presentModeCount != 0) {
|
||||
details.PresentModes.resize(presentModeCount);
|
||||
vkGetPhysicalDeviceSurfacePresentModesKHR(device, g->VulkanSurface, &presentModeCount, details.PresentModes.data());
|
||||
vkGetPhysicalDeviceSurfacePresentModesKHR(
|
||||
device, g->VulkanSurface, &presentModeCount, details.PresentModes.data());
|
||||
}
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
VkSurfaceFormatKHR device_choose_swap_surface_format(const std::vector<VkSurfaceFormatKHR>& availableFormats)
|
||||
VkSurfaceFormatKHR device_choose_swap_surface_format(
|
||||
const std::vector<VkSurfaceFormatKHR>& availableFormats)
|
||||
{
|
||||
for (const auto& availableFormat : availableFormats) {
|
||||
if (
|
||||
availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
||||
if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB
|
||||
&& availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
||||
return availableFormat;
|
||||
}
|
||||
}
|
||||
@@ -49,7 +55,8 @@ VkSurfaceFormatKHR device_choose_swap_surface_format(const std::vector<VkSurface
|
||||
return availableFormats[0];
|
||||
}
|
||||
|
||||
VkPresentModeKHR device_choose_swap_present_mode(const std::vector<VkPresentModeKHR>& availablePresentModes)
|
||||
VkPresentModeKHR device_choose_swap_present_mode(
|
||||
const std::vector<VkPresentModeKHR>& availablePresentModes)
|
||||
{
|
||||
for (const auto& availablePresentMode : availablePresentModes) {
|
||||
if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
|
||||
@@ -60,19 +67,18 @@ VkPresentModeKHR device_choose_swap_present_mode(const std::vector<VkPresentMode
|
||||
return VK_PRESENT_MODE_FIFO_KHR;
|
||||
}
|
||||
|
||||
VkExtent2D device_choose_swap_extent(const VkSurfaceCapabilitiesKHR& capabilities, int width, int height)
|
||||
VkExtent2D device_choose_swap_extent(
|
||||
const VkSurfaceCapabilitiesKHR& capabilities, int width, int height)
|
||||
{
|
||||
if (capabilities.currentExtent.width != UINT32_MAX) {
|
||||
return capabilities.currentExtent;
|
||||
} else {
|
||||
VkExtent2D actualExtent = { (uint32_t)width, (uint32_t)height };
|
||||
|
||||
actualExtent.width = std::max(
|
||||
capabilities.minImageExtent.width,
|
||||
actualExtent.width = std::max(capabilities.minImageExtent.width,
|
||||
std::min(capabilities.maxImageExtent.width, actualExtent.width));
|
||||
|
||||
actualExtent.height = std::max(
|
||||
capabilities.minImageExtent.height,
|
||||
actualExtent.height = std::max(capabilities.minImageExtent.height,
|
||||
std::min(capabilities.maxImageExtent.height, actualExtent.height));
|
||||
|
||||
return actualExtent;
|
||||
@@ -83,14 +89,19 @@ SwapChain* swapchain_create(GraphicsDevice* device, glm::ivec2 surface_size)
|
||||
{
|
||||
SwapChain* swapchain = new SwapChain();
|
||||
|
||||
SwapChainSupportDetails swapChainSupport = device_get_swapchain_support(device, device->VulkanPhysicalDevice);
|
||||
SwapChainSupportDetails swapChainSupport
|
||||
= device_get_swapchain_support(device, device->VulkanPhysicalDevice);
|
||||
|
||||
VkSurfaceFormatKHR surfaceFormat = device_choose_swap_surface_format(swapChainSupport.Formats);
|
||||
VkPresentModeKHR presentMode = device_choose_swap_present_mode(swapChainSupport.PresentModes);
|
||||
VkExtent2D extent = device_choose_swap_extent(swapChainSupport.Capabilities, surface_size.x, surface_size.y);
|
||||
VkSurfaceFormatKHR surfaceFormat
|
||||
= device_choose_swap_surface_format(swapChainSupport.Formats);
|
||||
VkPresentModeKHR presentMode
|
||||
= device_choose_swap_present_mode(swapChainSupport.PresentModes);
|
||||
VkExtent2D extent = device_choose_swap_extent(
|
||||
swapChainSupport.Capabilities, surface_size.x, surface_size.y);
|
||||
uint32_t imageCount = swapChainSupport.Capabilities.minImageCount + 1;
|
||||
|
||||
if (swapChainSupport.Capabilities.maxImageCount > 0 && imageCount > swapChainSupport.Capabilities.maxImageCount) {
|
||||
if (swapChainSupport.Capabilities.maxImageCount > 0
|
||||
&& imageCount > swapChainSupport.Capabilities.maxImageCount) {
|
||||
imageCount = swapChainSupport.Capabilities.maxImageCount;
|
||||
}
|
||||
|
||||
@@ -105,8 +116,10 @@ SwapChain* swapchain_create(GraphicsDevice* device, glm::ivec2 surface_size)
|
||||
createInfo.imageArrayLayers = 1;
|
||||
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
|
||||
QueueFamilyIndices indices = device_get_queue_families(device, device->VulkanPhysicalDevice);
|
||||
uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
|
||||
QueueFamilyIndices indices
|
||||
= device_get_queue_families(device, device->VulkanPhysicalDevice);
|
||||
uint32_t queueFamilyIndices[]
|
||||
= { indices.graphicsFamily.value(), indices.presentFamily.value() };
|
||||
|
||||
if (indices.graphicsFamily != indices.presentFamily) {
|
||||
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
||||
@@ -124,15 +137,19 @@ SwapChain* swapchain_create(GraphicsDevice* device, glm::ivec2 surface_size)
|
||||
createInfo.clipped = VK_TRUE;
|
||||
createInfo.oldSwapchain = VK_NULL_HANDLE;
|
||||
|
||||
if (vkCreateSwapchainKHR(device->VulkanDevice, &createInfo, nullptr, &swapchain->Handle) != VK_SUCCESS) {
|
||||
if (vkCreateSwapchainKHR(
|
||||
device->VulkanDevice, &createInfo, nullptr, &swapchain->Handle)
|
||||
!= VK_SUCCESS) {
|
||||
yolo::error("failed to create swap chain!");
|
||||
exit(1);
|
||||
}
|
||||
yolo::info("Swap chain created");
|
||||
|
||||
vkGetSwapchainImagesKHR(device->VulkanDevice, swapchain->Handle, &imageCount, nullptr);
|
||||
vkGetSwapchainImagesKHR(
|
||||
device->VulkanDevice, swapchain->Handle, &imageCount, nullptr);
|
||||
swapchain->Images.resize(imageCount);
|
||||
vkGetSwapchainImagesKHR(device->VulkanDevice, swapchain->Handle, &imageCount, swapchain->Images.data());
|
||||
vkGetSwapchainImagesKHR(
|
||||
device->VulkanDevice, swapchain->Handle, &imageCount, swapchain->Images.data());
|
||||
|
||||
swapchain->ImageFormat = surfaceFormat.format;
|
||||
swapchain->Extent = extent;
|
||||
@@ -174,7 +191,9 @@ void swapchain_image_view_create(SwapChain* swapchain)
|
||||
createInfo.subresourceRange.baseArrayLayer = 0;
|
||||
createInfo.subresourceRange.layerCount = 1;
|
||||
|
||||
if (vkCreateImageView(swapchain->Device->VulkanDevice, &createInfo, nullptr, &swapchain->ImageViews[i]) != VK_SUCCESS) {
|
||||
if (vkCreateImageView(swapchain->Device->VulkanDevice, &createInfo, nullptr,
|
||||
&swapchain->ImageViews[i])
|
||||
!= VK_SUCCESS) {
|
||||
yolo::error("failed to create image views!");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
// #include "gui/layout.hpp"
|
||||
// #include "renderer/renderer.hpp"
|
||||
// #include "scene/scene.hpp"
|
||||
#include "window.hpp"
|
||||
#include "graphics/pipeline.hpp"
|
||||
#include "graphics/device.hpp"
|
||||
#include "graphics/pipeline.hpp"
|
||||
#include "graphics/renderpass.hpp"
|
||||
#include "window.hpp"
|
||||
|
||||
// #include "preview_renderer/debug.hpp"
|
||||
// #include "preview_renderer/renderer.hpp"
|
||||
@@ -37,16 +38,15 @@ InfernoTimer* inferno_timer_create()
|
||||
return timer;
|
||||
}
|
||||
|
||||
void inferno_timer_cleanup(InfernoTimer* timer)
|
||||
{
|
||||
delete timer;
|
||||
}
|
||||
void inferno_timer_cleanup(InfernoTimer* timer) { delete timer; }
|
||||
|
||||
void inferno_timer_rolling_average(InfernoTimer* timer, int count)
|
||||
{
|
||||
if (timer->Times.size() > count)
|
||||
timer->Times.erase(timer->Times.begin());
|
||||
timer->RollingAverage = std::accumulate(timer->Times.begin(), timer->Times.end(), std::chrono::duration<double>(0.0)) / count;
|
||||
timer->RollingAverage = std::accumulate(timer->Times.begin(), timer->Times.end(),
|
||||
std::chrono::duration<double>(0.0))
|
||||
/ count;
|
||||
}
|
||||
|
||||
void inferno_timer_start(InfernoTimer* timer)
|
||||
@@ -94,7 +94,7 @@ InfernoApp* inferno_create()
|
||||
// Create window
|
||||
graphics::window_create("Inferno v" INFERNO_VERSION, 1920, 1080);
|
||||
graphics::GraphicsDevice* device = graphics::device_create();
|
||||
graphics::Pipeline* p = graphics::pipeline_create(device);
|
||||
graphics::RenderPass* p = graphics::renderpass_create(device);
|
||||
|
||||
// // setup the scene
|
||||
// scene::Material* basicMaterial = new scene::Material("basic");
|
||||
@@ -150,12 +150,12 @@ void inferno_preset_gui(InfernoApp* app)
|
||||
// ImGuiID dockspace_id = ImGui::GetID("main");
|
||||
//
|
||||
// ImGui::DockBuilderRemoveNode(dockspace_id); // Clear out existing layout
|
||||
// ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace); // Add empty node
|
||||
// ImGui::DockBuilderSetNodeSize(dockspace_id, { 1000, 1000 });
|
||||
// ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace); // Add empty
|
||||
// node ImGui::DockBuilderSetNodeSize(dockspace_id, { 1000, 1000 });
|
||||
//
|
||||
// ImGuiID dock_main_id = dockspace_id;
|
||||
// ImGuiID dock_left = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Left, 0.5f, NULL, &dock_main_id);
|
||||
// ImGui::DockBuilderDockWindow("Preview", dock_left);
|
||||
// ImGuiID dock_left = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Left, 0.5f,
|
||||
// NULL, &dock_main_id); ImGui::DockBuilderDockWindow("Preview", dock_left);
|
||||
// ImGui::DockBuilderDockWindow("Render", dock_main_id);
|
||||
// ImGui::DockBuilderFinish(dockspace_id);
|
||||
//
|
||||
@@ -171,13 +171,16 @@ void inferno_move_input(InfernoApp* app, std::chrono::duration<double> deltaTime
|
||||
// pan only get on hold
|
||||
static glm::dvec2 lastMousePos;
|
||||
static int firstClick = 0;
|
||||
if (glfwGetMouseButton(graphics::window_get_glfw_window(), GLFW_MOUSE_BUTTON_1) == GLFW_PRESS) {
|
||||
if (glfwGetMouseButton(graphics::window_get_glfw_window(), GLFW_MOUSE_BUTTON_1)
|
||||
== GLFW_PRESS) {
|
||||
firstClick++;
|
||||
if (firstClick == 1) {
|
||||
glfwGetCursorPos(graphics::window_get_glfw_window(), &lastMousePos.x, &lastMousePos.y);
|
||||
glfwGetCursorPos(
|
||||
graphics::window_get_glfw_window(), &lastMousePos.x, &lastMousePos.y);
|
||||
}
|
||||
glm::dvec2 tempMousePos = { 0.0f, 0.0f };
|
||||
glfwGetCursorPos(graphics::window_get_glfw_window(), &tempMousePos.x, &tempMousePos.y);
|
||||
glfwGetCursorPos(
|
||||
graphics::window_get_glfw_window(), &tempMousePos.x, &tempMousePos.y);
|
||||
app->Input->MouseDelta = lastMousePos - tempMousePos;
|
||||
lastMousePos = tempMousePos;
|
||||
} else {
|
||||
@@ -211,7 +214,8 @@ bool inferno_pre(InfernoApp* app)
|
||||
{
|
||||
app->FrameCount++;
|
||||
if (app->FrameCount % 100 == 0) {
|
||||
yolo::info("Average FPS: {}", 1.0 / inferno_timer_get_time(app->MainTimer).count());
|
||||
yolo::info(
|
||||
"Average FPS: {}", 1.0 / inferno_timer_get_time(app->MainTimer).count());
|
||||
inferno_timer_print(app->MainTimer, false);
|
||||
}
|
||||
|
||||
@@ -229,9 +233,7 @@ bool inferno_pre(InfernoApp* app)
|
||||
return true;
|
||||
}
|
||||
|
||||
void inferno_end(InfernoApp* app)
|
||||
{
|
||||
}
|
||||
void inferno_end(InfernoApp* app) { }
|
||||
|
||||
int inferno_run(InfernoApp* app)
|
||||
{
|
||||
@@ -241,7 +243,8 @@ int inferno_run(InfernoApp* app)
|
||||
// break;
|
||||
//
|
||||
// if (glm::length(app->Input->MouseDelta) > 0.0f)
|
||||
// graphics::camera_mouse_move(app->Scene->Camera, app->Input->MouseDelta);
|
||||
// graphics::camera_mouse_move(app->Scene->Camera,
|
||||
// app->Input->MouseDelta);
|
||||
// if (app->Input->MovementDelta != 0b00000000)
|
||||
// graphics::camera_move(app->Scene->Camera, app->Input->MovementDelta);
|
||||
//
|
||||
@@ -289,7 +292,8 @@ int inferno_run(InfernoApp* app)
|
||||
// ImGui::End();
|
||||
// }
|
||||
//
|
||||
// if (showPreview && ImGui::Begin("Preview", nullptr, ImGuiWindowFlags_NoScrollbar)) {
|
||||
// if (showPreview && ImGui::Begin("Preview", nullptr,
|
||||
// ImGuiWindowFlags_NoScrollbar)) {
|
||||
// if (ImGui::IsWindowHovered()) {
|
||||
// inferno_move_input(app, inferno_timer_get_time(app->MainTimer));
|
||||
// } else {
|
||||
@@ -298,12 +302,14 @@ int inferno_run(InfernoApp* app)
|
||||
//
|
||||
// graphics::camera_raster_set_viewport(scene::scene_get_camera(app->Scene),
|
||||
// { ImGui::GetWindowSize().x, ImGui::GetWindowSize().y });
|
||||
// graphics::preview_set_viewport(app->PreviewRenderer, app->Scene->Camera);
|
||||
// graphics::preview_set_viewport(app->PreviewRenderer,
|
||||
// app->Scene->Camera);
|
||||
//
|
||||
// graphics::preview_draw(app->PreviewRenderer, app->Scene);
|
||||
// graphics::debug_draw_to_target(app->Scene);
|
||||
//
|
||||
// ImTextureID texture = (ImTextureID)graphics::preview_get_rendered_texture(app->PreviewRenderer);
|
||||
// ImTextureID texture =
|
||||
// (ImTextureID)graphics::preview_get_rendered_texture(app->PreviewRenderer);
|
||||
// ImGui::Image(
|
||||
// texture,
|
||||
// { ImGui::GetWindowSize().x, ImGui::GetWindowSize().y },
|
||||
@@ -315,7 +321,8 @@ int inferno_run(InfernoApp* app)
|
||||
// if (ImGui::Begin("Render")) {
|
||||
// graphics::rayr_draw(app->RayRenderer);
|
||||
//
|
||||
// ImTextureID texture = (ImTextureID)graphics::rayr_get_rendered_texture(app->RayRenderer);
|
||||
// ImTextureID texture =
|
||||
// (ImTextureID)graphics::rayr_get_rendered_texture(app->RayRenderer);
|
||||
// ImGui::Image(
|
||||
// texture,
|
||||
// { ImGui::GetWindowSize().x, ImGui::GetWindowSize().y },
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "yolo/yolo.hpp"
|
||||
|
||||
namespace inferno::graphics {
|
||||
|
||||
static std::unordered_map<GLuint, int> shader2Index = {
|
||||
{ GL_VERTEX_SHADER, 0 },
|
||||
{ GL_GEOMETRY_SHADER, 1 },
|
||||
{ GL_FRAGMENT_SHADER, 2 }
|
||||
};
|
||||
static std::unordered_map<GLuint, int> shader2Index = {};
|
||||
|
||||
inline std::string trim(std::string& str)
|
||||
{
|
||||
@@ -25,164 +23,123 @@ std::string textFromFile(const std::filesystem::path& path)
|
||||
std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
std::vector<const ShaderPreprocessorDefinition*> getKeys(Shader* shader, std::string key)
|
||||
{
|
||||
std::vector<const ShaderPreprocessorDefinition*> ret;
|
||||
for (const auto& p : shader->PreprocessorDefinitions)
|
||||
if (p.key == key)
|
||||
ret.push_back(&p);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool checkShader(GLuint uid)
|
||||
{
|
||||
GLint isCompiled = 0;
|
||||
glGetShaderiv(uid, GL_COMPILE_STATUS, &isCompiled);
|
||||
// glGetShaderiv(uid, GL_COMPILE_STATUS, &isCompiled);
|
||||
if (isCompiled == GL_FALSE) {
|
||||
GLint maxLength = 0;
|
||||
glGetShaderiv(uid, GL_INFO_LOG_LENGTH, &maxLength);
|
||||
// glGetShaderiv(uid, GL_INFO_LOG_LENGTH, &maxLength);
|
||||
|
||||
std::vector<GLchar> errorLog(maxLength);
|
||||
glGetShaderInfoLog(uid, maxLength, &maxLength, &errorLog[0]);
|
||||
// glGetShaderInfoLog(uid, maxLength, &maxLength, &errorLog[0]);
|
||||
|
||||
for (int i = 0; i < errorLog.size(); i++) {
|
||||
std::cout << errorLog[i];
|
||||
}
|
||||
|
||||
glDeleteShader(uid);
|
||||
// glDeleteShader(uid);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Shader* shader_create()
|
||||
Shader* shader_create(VkDevice device)
|
||||
{
|
||||
Shader* shader = new Shader;
|
||||
|
||||
shader->Program = 0;
|
||||
shader->Shaders[0] = GL_NONE;
|
||||
shader->Shaders[1] = GL_NONE;
|
||||
shader->Shaders[2] = GL_NONE;
|
||||
shader->Device = device;
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
void shader_cleanup(Shader* shader)
|
||||
{
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (shader->Shaders[i] == GL_NONE)
|
||||
continue;
|
||||
glDeleteShader(shader->Shaders[i]);
|
||||
}
|
||||
|
||||
glDeleteProgram(shader->Program);
|
||||
vkDestroyShaderModule(shader->Device, shader->VertexShader, nullptr);
|
||||
vkDestroyShaderModule(shader->Device, shader->FragmentShader, nullptr);
|
||||
}
|
||||
|
||||
void shader_load(Shader* shader, std::filesystem::path path)
|
||||
{
|
||||
|
||||
assert(std::filesystem::exists(path));
|
||||
|
||||
std::string loadedShaderSource = textFromFile(path);
|
||||
// path is the filename, code needs to add .vert.spv or .frag.spv
|
||||
// std::string shaderPath = "shaders/" + path + ".spv";
|
||||
|
||||
for (int i = 0; i < loadedShaderSource.length(); i++) {
|
||||
const char& c = loadedShaderSource[i];
|
||||
if (c == '#') {
|
||||
ShaderPreprocessorDefinition def = { .start = i };
|
||||
int j;
|
||||
for (j = ++i; loadedShaderSource[j] != ' '; j++) {
|
||||
def.key += loadedShaderSource[j];
|
||||
}
|
||||
for (j++; loadedShaderSource[j] != '\n'; j++) {
|
||||
def.def += loadedShaderSource[j];
|
||||
}
|
||||
def.end = j;
|
||||
i = j; // advance i
|
||||
def.def = trim(def.def);
|
||||
def.key = trim(def.key);
|
||||
shader->PreprocessorDefinitions.push_back(def);
|
||||
}
|
||||
std::string shaderPath = path.string();
|
||||
std::string vertexShaderPath = shaderPath + ".vert.spv";
|
||||
std::string fragmentShaderPath = shaderPath + ".frag.spv";
|
||||
assert(std::filesystem::exists(vertexShaderPath));
|
||||
assert(std::filesystem::exists(fragmentShaderPath));
|
||||
|
||||
std::string vertexLoadedShaderCode = textFromFile(vertexShaderPath);
|
||||
VkShaderModuleCreateInfo createInfo = {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
createInfo.codeSize = vertexLoadedShaderCode.size();
|
||||
createInfo.pCode
|
||||
= reinterpret_cast<const uint32_t*>(vertexLoadedShaderCode.data());
|
||||
|
||||
if (vkCreateShaderModule(
|
||||
shader->Device, &createInfo, nullptr, &shader->VertexShader)
|
||||
!= VK_SUCCESS) {
|
||||
yolo::error("failed to create shader module");
|
||||
}
|
||||
|
||||
// now we have all of the key/value definitions
|
||||
// we can extract the relavent ones, for example
|
||||
// "type"
|
||||
std::vector<const ShaderPreprocessorDefinition*> types = getKeys(shader, "type");
|
||||
int i = 0;
|
||||
for (const ShaderPreprocessorDefinition* type : types) {
|
||||
GLuint glType = GL_NONE;
|
||||
if (type->def == "vertex")
|
||||
glType = GL_VERTEX_SHADER;
|
||||
if (type->def == "geometry")
|
||||
glType = GL_GEOMETRY_SHADER;
|
||||
if (type->def == "fragment")
|
||||
glType = GL_FRAGMENT_SHADER;
|
||||
shader->ShaderStages[0].sType
|
||||
= VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
shader->ShaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
shader->ShaderStages[0].module = shader->VertexShader;
|
||||
shader->ShaderStages[0].pName = "main";
|
||||
|
||||
assert(glType != GL_NONE);
|
||||
std::string fragmentLoadedShaderCode = textFromFile(fragmentShaderPath);
|
||||
VkShaderModuleCreateInfo createInfo2 = {};
|
||||
createInfo2.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
createInfo2.codeSize = fragmentLoadedShaderCode.size();
|
||||
createInfo2.pCode
|
||||
= reinterpret_cast<const uint32_t*>(fragmentLoadedShaderCode.data());
|
||||
|
||||
shader->Shaders[shader2Index[glType]] = glCreateShader(glType);
|
||||
|
||||
const char* source = loadedShaderSource.c_str() + type->end;
|
||||
int end = types.size() - 1 == i ? types.size() : types[i + 1]->start;
|
||||
int length = end - type->end;
|
||||
|
||||
glShaderSource(shader->Shaders[shader2Index[glType]], 1, &source, &length);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void shader_link(Shader* shader)
|
||||
{
|
||||
shader->Program = glCreateProgram();
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (shader->Shaders[i] == GL_NONE)
|
||||
continue;
|
||||
|
||||
glCompileShader(shader->Shaders[i]);
|
||||
if (!checkShader(shader->Shaders[i]))
|
||||
continue;
|
||||
|
||||
glAttachShader(shader->Program, shader->Shaders[i]);
|
||||
if (vkCreateShaderModule(
|
||||
shader->Device, &createInfo2, nullptr, &shader->FragmentShader)
|
||||
!= VK_SUCCESS) {
|
||||
yolo::error("failed to create shader module");
|
||||
}
|
||||
|
||||
glLinkProgram(shader->Program);
|
||||
shader->ShaderStages[1].sType
|
||||
= VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
shader->ShaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
shader->ShaderStages[1].module = shader->FragmentShader;
|
||||
shader->ShaderStages[1].pName = "main";
|
||||
}
|
||||
|
||||
GLuint shader_get_program(Shader* shader)
|
||||
{
|
||||
return shader->Program;
|
||||
}
|
||||
|
||||
void shader_add_attribute(Shader* shader, const std::string& attribute)
|
||||
{
|
||||
shader->Attributes[attribute] = glGetAttribLocation(shader->Program, attribute.c_str());
|
||||
}
|
||||
|
||||
void shader_add_uniform(Shader* shader, const std::string& uniform)
|
||||
{
|
||||
shader->Uniforms[uniform] = glGetUniformLocation(shader->Program, uniform.c_str());
|
||||
}
|
||||
|
||||
GLuint shader_get_attribute(Shader* shader, const std::string& attribute)
|
||||
{
|
||||
return shader->Attributes[attribute];
|
||||
}
|
||||
|
||||
GLuint shader_get_uniform(Shader* shader, const std::string& uniform)
|
||||
{
|
||||
return shader->Uniforms[uniform];
|
||||
}
|
||||
// void shader_add_attribute(Shader* shader, const std::string& attribute)
|
||||
// {
|
||||
// // shader->Attributes[attribute] = glGetAttribLocation(shader->Program,
|
||||
// attribute.c_str());
|
||||
// }
|
||||
//
|
||||
// void shader_add_uniform(Shader* shader, const std::string& uniform)
|
||||
// {
|
||||
// shader->Uniforms[uniform] = glGetUniformLocation(shader->Program,
|
||||
// uniform.c_str());
|
||||
// }
|
||||
//
|
||||
// GLuint shader_get_attribute(Shader* shader, const std::string& attribute)
|
||||
// {
|
||||
// return shader->Attributes[attribute];
|
||||
// }
|
||||
//
|
||||
// GLuint shader_get_uniform(Shader* shader, const std::string& uniform)
|
||||
// {
|
||||
// return shader->Uniforms[uniform];
|
||||
// }
|
||||
|
||||
void shader_use(Shader* shader)
|
||||
{
|
||||
glUseProgram(shader->Program);
|
||||
// glUseProgram(shader->Program);
|
||||
}
|
||||
|
||||
void shader_unuse(Shader* shader)
|
||||
{
|
||||
glUseProgram(0);
|
||||
// glUseProgram(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,33 +10,23 @@
|
||||
|
||||
namespace inferno::graphics {
|
||||
|
||||
typedef struct ShaderPreprocessorDefinition {
|
||||
int start, end;
|
||||
std::string key;
|
||||
std::string def;
|
||||
} shaderpreprocessordefinition;
|
||||
|
||||
// TODO: Make general
|
||||
typedef struct Shader {
|
||||
GLuint Shaders[3];
|
||||
GLuint Program;
|
||||
std::unordered_map<std::string, GLuint> Attributes;
|
||||
std::unordered_map<std::string, GLuint> Uniforms;
|
||||
std::vector<ShaderPreprocessorDefinition> PreprocessorDefinitions;
|
||||
VkDevice Device;
|
||||
VkShaderModule VertexShader;
|
||||
VkShaderModule FragmentShader;
|
||||
VkPipelineShaderStageCreateInfo ShaderStages[2];
|
||||
VkPipelineVertexInputStateCreateInfo VertexInputInfo;
|
||||
} Shader;
|
||||
|
||||
Shader* shader_create();
|
||||
Shader* shader_create(VkDevice device);
|
||||
void shader_cleanup(Shader* shader);
|
||||
|
||||
void shader_load(Shader* shader, std::filesystem::path path);
|
||||
void shader_link(Shader* shader);
|
||||
|
||||
GLuint shader_get_program(Shader* shader);
|
||||
|
||||
// TODO: Implement shader_reload
|
||||
void shader_add_attribute(Shader* shader, const std::string& attribute);
|
||||
void shader_add_uniform(Shader* shader, const std::string& uniform);
|
||||
GLuint shader_get_attribute(Shader* shader, const std::string& attribute);
|
||||
GLuint shader_get_uniform(Shader* shader, const std::string& uniform);
|
||||
// although with vulkan, it's not really necessary
|
||||
// as the pipeline is created with the shaders
|
||||
|
||||
void shader_use(Shader* shader);
|
||||
void shader_unuse(Shader* shader);
|
||||
|
||||
Reference in New Issue
Block a user