feat(renderer/vk): dynamic rendering
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
97ca429d38
commit
736c37d2f1
11 changed files with 251 additions and 68 deletions
|
|
@ -75,10 +75,17 @@ void Device::initialize_logical_device()
|
|||
|
||||
auto extensions = std::vector<const char *> {
|
||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||
VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME,
|
||||
};
|
||||
|
||||
const auto dynamic_rendering_features = VkPhysicalDeviceDynamicRenderingFeatures {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES,
|
||||
.dynamicRendering = true,
|
||||
};
|
||||
|
||||
auto device_info = VkDeviceCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||
.pNext = &dynamic_rendering_features,
|
||||
.queueCreateInfoCount = static_cast<uint32_t>(queue_infos.size()),
|
||||
.pQueueCreateInfos = queue_infos.data(),
|
||||
.enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
|
||||
|
|
|
|||
|
|
@ -100,12 +100,16 @@ PFN_vkFreeMemory vk_free_memory {};
|
|||
|
||||
PFN_vkResetCommandBuffer vk_reset_command_buffer {};
|
||||
|
||||
PFN_vkCmdBeginRendering vk_cmd_begin_rendering {};
|
||||
PFN_vkCmdEndRendering vk_cmd_end_rendering {};
|
||||
|
||||
PFN_vkGetPhysicalDeviceSurfaceSupportKHR vk_get_physical_device_surface_support {};
|
||||
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vk_get_physical_device_surface_capabilities {};
|
||||
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vk_get_physical_device_surface_formats {};
|
||||
|
||||
auto vk_create_xlib_surface_khr = PFN_vkCreateXlibSurfaceKHR {};
|
||||
auto vk_destroy_surface_khr = PFN_vkDestroySurfaceKHR {};
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
Instance::Instance()
|
||||
|
|
@ -392,6 +396,9 @@ void Instance::load_device_functions_impl(VkDevice device)
|
|||
load_fn(vk_free_memory, "vkFreeMemory");
|
||||
load_fn(vk_get_buffer_memory_requirements, "vkGetBufferMemoryRequirements");
|
||||
load_fn(vk_reset_command_buffer, "vkResetCommandBuffer");
|
||||
|
||||
load_fn(vk_cmd_begin_rendering, "vkCmdBeginRendering");
|
||||
load_fn(vk_cmd_end_rendering, "vkCmdEndRendering");
|
||||
}
|
||||
|
||||
auto Instance::enumerate_gpus() const -> std::vector<VkPhysicalDevice>
|
||||
|
|
|
|||
|
|
@ -49,6 +49,17 @@ public:
|
|||
return m_images.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_image_view(uint32_t idx) -> VkImageView
|
||||
{
|
||||
return m_image_views[idx];
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_image(uint32_t idx) -> VkImage
|
||||
{
|
||||
return m_images[idx];
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]] auto create_framebuffers_for_pass(VkRenderPass pass) const
|
||||
-> std::vector<VkFramebuffer>;
|
||||
|
||||
|
|
|
|||
|
|
@ -113,16 +113,16 @@ Pass::Pass(
|
|||
.blendConstants = { 0.0f, 0.0, 0.0, 0.0 },
|
||||
};
|
||||
|
||||
auto attachment_description = VkAttachmentDescription {
|
||||
.format = static_cast<Swapchain *>(swapchain)->get_format(),
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
};
|
||||
// auto attachment_description = VkAttachmentDescription {
|
||||
// .format =,
|
||||
// .samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
// .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
// .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
// .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
// .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
// .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
// .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
// };
|
||||
|
||||
auto color_attachment_ref = VkAttachmentReference {
|
||||
.attachment = 0,
|
||||
|
|
@ -144,21 +144,18 @@ Pass::Pass(
|
|||
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
};
|
||||
|
||||
m_pass = m_device->create_pass(
|
||||
VkRenderPassCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||
.attachmentCount = 1u,
|
||||
.pAttachments = &attachment_description,
|
||||
.subpassCount = 1u,
|
||||
.pSubpasses = &subpass_description,
|
||||
.dependencyCount = 1u,
|
||||
.pDependencies = &pass_dependency,
|
||||
}
|
||||
);
|
||||
auto color_format = static_cast<Swapchain *>(swapchain)->get_format();
|
||||
auto rendering_info = VkPipelineRenderingCreateInfoKHR {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
|
||||
.colorAttachmentCount = 1u,
|
||||
.pColorAttachmentFormats = &color_format,
|
||||
|
||||
};
|
||||
|
||||
m_pipeline = m_device->create_graphics_pipeline(
|
||||
VkGraphicsPipelineCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.pNext = &rendering_info,
|
||||
.stageCount = static_cast<uint32_t>(shader_stages.size()),
|
||||
.pStages = shader_stages.data(),
|
||||
.pVertexInputState = &vertex_input,
|
||||
|
|
@ -170,14 +167,14 @@ Pass::Pass(
|
|||
.pColorBlendState = &color_blend,
|
||||
.pDynamicState = &dynamic_state,
|
||||
.layout = m_layout,
|
||||
.renderPass = m_pass,
|
||||
.renderPass = VK_NULL_HANDLE,
|
||||
.subpass = 0u,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = -1,
|
||||
}
|
||||
);
|
||||
|
||||
m_framebuffers = static_cast<Swapchain *>(swapchain)->create_framebuffers_for_pass(m_pass);
|
||||
// m_framebuffers = static_cast<Swapchain *>(swapchain)->create_framebuffers_for_pass(m_pass);
|
||||
|
||||
m_device->destroy_shader_module(vertex_module);
|
||||
m_device->destroy_shader_module(fragment_module);
|
||||
|
|
@ -193,7 +190,7 @@ Pass::~Pass()
|
|||
m_device->wait_idle();
|
||||
m_device->destroy_framebuffers(m_framebuffers);
|
||||
m_device->destroy_pipeline(m_pipeline);
|
||||
m_device->destroy_pass(m_pass);
|
||||
// m_device->destroy_pass(m_pass);
|
||||
m_device->destroy_pipeline_layout(m_layout);
|
||||
}
|
||||
|
||||
|
|
@ -206,7 +203,8 @@ void Pass::replace_swapchain(const ISwapchain &swapchain)
|
|||
|
||||
m_device->wait_idle();
|
||||
m_device->destroy_framebuffers(m_framebuffers);
|
||||
m_framebuffers = static_cast<const Swapchain &>(swapchain).create_framebuffers_for_pass(m_pass);
|
||||
// m_framebuffers = static_cast<const Swapchain
|
||||
// &>(swapchain).create_framebuffers_for_pass(m_pass);
|
||||
}
|
||||
|
||||
auto Pass::create_module(lt::assets::Blob blob) -> VkShaderModule
|
||||
|
|
|
|||
|
|
@ -29,11 +29,6 @@ public:
|
|||
|
||||
void replace_swapchain(const ISwapchain &swapchain);
|
||||
|
||||
[[nodiscard]] auto get_pass() -> VkRenderPass
|
||||
{
|
||||
return m_pass;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_pipeline() -> VkPipeline
|
||||
{
|
||||
return m_pipeline;
|
||||
|
|
@ -54,8 +49,6 @@ private:
|
|||
|
||||
memory::NullOnMove<class Device *> m_device {};
|
||||
|
||||
VkRenderPass m_pass = VK_NULL_HANDLE;
|
||||
|
||||
VkPipeline m_pipeline = VK_NULL_HANDLE;
|
||||
|
||||
VkPipelineLayout m_layout = VK_NULL_HANDLE;
|
||||
|
|
|
|||
|
|
@ -143,12 +143,79 @@ void Renderer::replace_swapchain(ISwapchain *swapchain)
|
|||
|
||||
void Renderer::record_cmd(VkCommandBuffer cmd, uint32_t image_idx)
|
||||
{
|
||||
auto cmd_begin_info = VkCommandBufferBeginInfo {
|
||||
const auto cmd_begin_info = VkCommandBufferBeginInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = {},
|
||||
.pInheritanceInfo = nullptr,
|
||||
};
|
||||
|
||||
const auto begin_frame_barrier = VkImageMemoryBarrier {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = {},
|
||||
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
.image = m_swapchain->get_image(image_idx),
|
||||
.subresourceRange = VkImageSubresourceRange{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0u,
|
||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||
.baseArrayLayer = 0u,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
const auto end_frame_barrier = VkImageMemoryBarrier {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.dstAccessMask = {},
|
||||
.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
.image = m_swapchain->get_image(image_idx),
|
||||
|
||||
.subresourceRange = VkImageSubresourceRange{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0u,
|
||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||
.baseArrayLayer = 0u,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
},
|
||||
};
|
||||
|
||||
const auto scissor = VkRect2D {
|
||||
.offset = { .x = 0u, .y = 0u },
|
||||
.extent = m_resolution,
|
||||
};
|
||||
|
||||
const auto viewport = VkViewport {
|
||||
.x = 0.0f,
|
||||
.y = 0.0f,
|
||||
.width = static_cast<float>(m_resolution.width),
|
||||
.height = static_cast<float>(m_resolution.height),
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f,
|
||||
};
|
||||
|
||||
const auto color_attachment_info = VkRenderingAttachmentInfoKHR {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
|
||||
.imageView = m_swapchain->get_image_view(image_idx),
|
||||
.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
.resolveMode = VK_RESOLVE_MODE_NONE,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.clearValue = VkClearValue { .color = { 0.93, 0.93, 0.93, 1.0 } },
|
||||
};
|
||||
|
||||
const auto rendering_info = VkRenderingInfoKHR {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO_KHR,
|
||||
.renderArea = scissor,
|
||||
.layerCount = 1,
|
||||
.colorAttachmentCount = 1,
|
||||
.pColorAttachments = &color_attachment_info,
|
||||
|
||||
};
|
||||
|
||||
vkc(vk_begin_command_buffer(cmd, &cmd_begin_info));
|
||||
vk_cmd_push_constants(
|
||||
cmd,
|
||||
|
|
@ -158,46 +225,41 @@ void Renderer::record_cmd(VkCommandBuffer cmd, uint32_t image_idx)
|
|||
sizeof(FrameConstants),
|
||||
&m_frame_constants
|
||||
);
|
||||
|
||||
auto clear_value = VkClearValue {
|
||||
.color = {
|
||||
0.93,
|
||||
0.93,
|
||||
0.93,
|
||||
1.0,
|
||||
},
|
||||
};
|
||||
|
||||
auto pass_begin_info = VkRenderPassBeginInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.renderPass = m_pass->get_pass(),
|
||||
.framebuffer = m_pass->get_framebuffers()[image_idx],
|
||||
.renderArea = { .offset = {}, .extent = m_resolution },
|
||||
.clearValueCount = 1u,
|
||||
.pClearValues = &clear_value
|
||||
};
|
||||
vk_cmd_begin_render_pass(cmd, &pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
|
||||
vk_cmd_pipeline_barrier(
|
||||
cmd,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
0,
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
1,
|
||||
&begin_frame_barrier
|
||||
);
|
||||
vk_cmd_begin_rendering(cmd, &rendering_info);
|
||||
vk_cmd_bind_pipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pass->get_pipeline());
|
||||
|
||||
auto viewport = VkViewport {
|
||||
.x = 0.0f,
|
||||
.y = 0.0f,
|
||||
.width = static_cast<float>(m_resolution.width),
|
||||
.height = static_cast<float>(m_resolution.height),
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f,
|
||||
};
|
||||
vk_cmd_set_viewport(cmd, 0, 1, &viewport);
|
||||
|
||||
auto scissor = VkRect2D {
|
||||
.offset = { .x = 0u, .y = 0u },
|
||||
.extent = m_resolution,
|
||||
};
|
||||
vk_cmd_set_scissors(cmd, 0, 1, &scissor);
|
||||
|
||||
vk_cmd_draw(cmd, 3, 1, 0, 0);
|
||||
vk_cmd_end_render_pass(cmd);
|
||||
vk_cmd_end_rendering(cmd);
|
||||
vk_cmd_pipeline_barrier(
|
||||
cmd,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
||||
0,
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
1,
|
||||
&end_frame_barrier
|
||||
);
|
||||
vkc(vk_end_command_buffer(cmd));
|
||||
}
|
||||
|
||||
void submit_sprite(const components::Sprite &sprite, const math::components::Transform &transform)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace lt::renderer::vk
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@
|
|||
#include <memory/reference.hpp>
|
||||
#include <ranges>
|
||||
#include <renderer/backend/vk/context/device.hpp>
|
||||
#include <renderer/backend/vk/data/buffer.hpp>
|
||||
#include <renderer/backend/vk/renderer/pass.hpp>
|
||||
#include <renderer/backend/vk/utils.hpp>
|
||||
#include <renderer/frontend/data/buffer.hpp>
|
||||
#include <renderer/frontend/renderer/pass.hpp>
|
||||
#include <renderer/frontend/renderer/renderer.hpp>
|
||||
|
||||
|
|
@ -34,6 +36,13 @@ public:
|
|||
m_frame_constants = constants;
|
||||
}
|
||||
|
||||
void submit_sprite(
|
||||
const components::Sprite &sprite,
|
||||
const math::components::Transform &transform
|
||||
) override
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
void record_cmd(VkCommandBuffer cmd, uint32_t image_idx);
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,9 @@ extern PFN_vkUnmapMemory vk_unmap_memory;
|
|||
extern PFN_vkFreeMemory vk_free_memory;
|
||||
|
||||
extern PFN_vkResetCommandBuffer vk_reset_command_buffer;
|
||||
|
||||
extern PFN_vkCmdBeginRendering vk_cmd_begin_rendering;
|
||||
extern PFN_vkCmdEndRendering vk_cmd_end_rendering;
|
||||
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
} // namespace lt::renderer::vk
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <math/components/transform.hpp>
|
||||
#include <memory/scope.hpp>
|
||||
#include <renderer/api.hpp>
|
||||
#include <renderer/components/sprite.hpp>
|
||||
#include <renderer/data/frame_constants.hpp>
|
||||
|
||||
namespace lt::renderer {
|
||||
|
|
@ -44,6 +46,11 @@ public:
|
|||
virtual void replace_swapchain(class ISwapchain *swapchain) = 0;
|
||||
|
||||
virtual void set_frame_constants(FrameConstants constants) = 0;
|
||||
|
||||
virtual void submit_sprite(
|
||||
const components::Sprite &sprite,
|
||||
const math::components::Transform &transform
|
||||
) = 0;
|
||||
};
|
||||
|
||||
} // namespace lt::renderer
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include <camera/components.hpp>
|
||||
#include <math/algebra.hpp>
|
||||
#include <math/components/transform.hpp>
|
||||
#include <renderer/components/messenger.hpp>
|
||||
#include <renderer/components/sprite.hpp>
|
||||
#include <renderer/frontend/context/device.hpp>
|
||||
#include <renderer/frontend/context/gpu.hpp>
|
||||
#include <renderer/frontend/context/instance.hpp>
|
||||
|
|
@ -88,6 +90,13 @@ void System::tick(app::TickInfo tick)
|
|||
}
|
||||
}
|
||||
|
||||
// for each sprite, submit a new "model matrix" + "color" to go into the scene's SSBO
|
||||
for (auto &[id, sprite, transform] :
|
||||
m_registry->view<components::Sprite, math::components::Transform>())
|
||||
{
|
||||
m_renderer->submit_sprite(sprite, transform);
|
||||
}
|
||||
|
||||
m_renderer->set_frame_constants({ .view_projection = perspective });
|
||||
if (m_renderer->draw(m_frame_idx) != IRenderer::DrawResult::success)
|
||||
{
|
||||
|
|
|
|||
77
modules/renderer/public/components/sprite.hpp
Normal file
77
modules/renderer/public/components/sprite.hpp
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#pragma once
|
||||
|
||||
#include <assets/shader.hpp>
|
||||
#include <math/vec3.hpp>
|
||||
#include <memory/reference.hpp>
|
||||
|
||||
namespace lt::renderer::components {
|
||||
|
||||
enum class VertexFormat : uint8_t
|
||||
{
|
||||
r32_g32_b32_sfloat,
|
||||
|
||||
r32_g32_sfloat,
|
||||
};
|
||||
|
||||
enum class VertexInputRate : uint8_t
|
||||
{
|
||||
per_vertex,
|
||||
|
||||
per_instance,
|
||||
};
|
||||
|
||||
struct VertexInputAttributeDescriptipn
|
||||
{
|
||||
uint32_t location;
|
||||
|
||||
uint32_t binding;
|
||||
|
||||
uint32_t offset;
|
||||
|
||||
VertexFormat format;
|
||||
};
|
||||
|
||||
struct VertexInputBindingDescription
|
||||
{
|
||||
uint32_t binding;
|
||||
|
||||
uint32_t stride;
|
||||
};
|
||||
|
||||
/** Requires a math::components::Transform component on the same entity to be functional. */
|
||||
struct Sprite
|
||||
{
|
||||
struct Vertex
|
||||
{
|
||||
math::vec3 position;
|
||||
|
||||
math::vec3 color;
|
||||
|
||||
[[nodiscard]] constexpr static auto get_attributes()
|
||||
-> std::array<VertexInputAttributeDescriptipn, 2>
|
||||
{
|
||||
return {
|
||||
VertexInputAttributeDescriptipn {
|
||||
.location = 0u,
|
||||
.binding = 0u,
|
||||
.offset = offsetof(Sprite::Vertex, position),
|
||||
.format = VertexFormat::r32_g32_b32_sfloat,
|
||||
|
||||
},
|
||||
|
||||
VertexInputAttributeDescriptipn {
|
||||
.location = 1u,
|
||||
.binding = 0u,
|
||||
.offset = offsetof(Sprite::Vertex, color),
|
||||
.format = VertexFormat::r32_g32_b32_sfloat,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
memory::Ref<assets::ShaderAsset> vertex_shader;
|
||||
|
||||
memory::Ref<assets::ShaderAsset> fragment_shader;
|
||||
};
|
||||
|
||||
} // namespace lt::renderer::components
|
||||
Loading…
Add table
Reference in a new issue