Compare commits
12 commits
607e6864b4
...
4976773218
| Author | SHA1 | Date | |
|---|---|---|---|
| 4976773218 | |||
| 5148b8836c | |||
| 405c707e23 | |||
| 21a82ff57d | |||
| a46f36aefd | |||
| 723ade84ea | |||
| cce627a350 | |||
| a77abe312b | |||
| 84d0026051 | |||
| 34fa8344ac | |||
| fa1bfaae1e | |||
| d411c9ab2c |
114 changed files with 2274 additions and 5343 deletions
|
|
@ -8,6 +8,7 @@ using lt::test::expect_unreachable;
|
|||
using lt::test::Suite;
|
||||
|
||||
using lt::test::expect_eq;
|
||||
using lt::test::expect_ne;
|
||||
|
||||
using lt::test::expect_false;
|
||||
using lt::test::expect_true;
|
||||
|
|
@ -62,12 +63,12 @@ struct std::formatter<Component_B>
|
|||
}
|
||||
};
|
||||
|
||||
Suite raii = [] {
|
||||
Suite raii = "raii"_suite = [] {
|
||||
Case { "happy path won't throw" } = [] {
|
||||
std::ignore = Registry {};
|
||||
};
|
||||
|
||||
Case { "many won't throw" } = [] {
|
||||
Case { "many won't freeze/throw" } = [] {
|
||||
for (auto idx : std::views::iota(0, 100'000))
|
||||
{
|
||||
std::ignore = Registry {};
|
||||
|
|
@ -83,7 +84,7 @@ Suite raii = [] {
|
|||
};
|
||||
};
|
||||
|
||||
Suite entity_raii = [] {
|
||||
Suite entity_raii = "entity_raii"_suite = [] {
|
||||
Case { "create_entity returns unique values" } = [] {
|
||||
auto registry = Registry {};
|
||||
auto set = std::unordered_set<EntityId> {};
|
||||
|
|
@ -119,7 +120,7 @@ Suite entity_raii = [] {
|
|||
};
|
||||
};
|
||||
|
||||
Suite component_raii = [] {
|
||||
Suite component_raii = "component_raii"_suite = [] {
|
||||
Case { "add has correct state" } = [] {
|
||||
auto registry = Registry {};
|
||||
for (auto idx : std::views::iota(0, 100'000))
|
||||
|
|
@ -130,7 +131,7 @@ Suite component_raii = [] {
|
|||
{ .m_int = idx, .m_string = std::to_string(idx) }
|
||||
);
|
||||
|
||||
expect_eq(component.m_int, idx);
|
||||
expect_ne(component.m_int, idx);
|
||||
expect_eq(component.m_string, std::to_string(idx));
|
||||
}
|
||||
};
|
||||
|
|
@ -151,7 +152,7 @@ Suite component_raii = [] {
|
|||
};
|
||||
};
|
||||
|
||||
Suite callbacks = [] {
|
||||
Suite callbacks = "callbacks"_suite = [] {
|
||||
Case { "connecting on_construct/on_destruct won't throw" } = [] {
|
||||
auto registry = Registry {};
|
||||
registry.connect_on_construct<Component>([&](Registry &, EntityId) {});
|
||||
|
|
@ -205,7 +206,7 @@ Suite callbacks = [] {
|
|||
};
|
||||
};
|
||||
|
||||
Suite each = [] {
|
||||
Suite each = "each"_suite = [] {
|
||||
auto registry = Registry {};
|
||||
|
||||
auto shared_entity_counter = 0u;
|
||||
|
|
@ -276,7 +277,7 @@ Suite each = [] {
|
|||
};
|
||||
};
|
||||
|
||||
Suite views = [] {
|
||||
Suite views = "views"_suite = [] {
|
||||
auto registry = Registry {};
|
||||
|
||||
auto shared_entity_counter = 0u;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ using lt::test::expect_true;
|
|||
using Set = lt::ecs::SparseSet<int>;
|
||||
constexpr auto capacity = 100;
|
||||
|
||||
Suite raii = [] {
|
||||
Suite raii = "raii"_suite = [] {
|
||||
Case { "happy path won't throw" } = [] {
|
||||
std::ignore = Set {};
|
||||
std::ignore = Set { Set::max_capacity };
|
||||
|
|
@ -32,8 +32,8 @@ Suite raii = [] {
|
|||
};
|
||||
};
|
||||
|
||||
Suite element_raii = [] {
|
||||
Case { "many inserts/removes won't throw" } = [] {
|
||||
Suite element_raii = "element_raii"_suite = [] {
|
||||
Case { "many inserts/removes won't freeze/throw" } = [] {
|
||||
auto set = Set {};
|
||||
for (auto idx : std::views::iota(0, 10'000))
|
||||
{
|
||||
|
|
@ -107,7 +107,7 @@ Suite element_raii = [] {
|
|||
};
|
||||
};
|
||||
|
||||
Suite getters = [] {
|
||||
Suite getters = "getters"_suite = [] {
|
||||
Case { "get_size returns correct values" } = [] {
|
||||
auto set = Set {};
|
||||
for (auto idx : std::views::iota(0, 10'000))
|
||||
|
|
@ -149,7 +149,7 @@ Suite getters = [] {
|
|||
};
|
||||
};
|
||||
|
||||
Suite clear = [] {
|
||||
Suite clear = "clear"_suite = [] {
|
||||
Case { "post clear has correct state" } = [] {
|
||||
auto set = Set { 0 };
|
||||
for (auto idx : std::views::iota(0, 10'000))
|
||||
|
|
|
|||
|
|
@ -15,6 +15,12 @@ public:
|
|||
ensure(m_registry, "Failed to create Entity ({}): null registry", m_identifier);
|
||||
}
|
||||
|
||||
template<typename Component_T>
|
||||
auto add(Component_T component) -> Component_T &
|
||||
{
|
||||
return m_registry->add(m_identifier, component);
|
||||
}
|
||||
|
||||
template<typename Component_T>
|
||||
auto get() -> Component_T &
|
||||
{
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ private:
|
|||
Ref<ecs::Registry> m_registry = create_ref<ecs::Registry>();
|
||||
};
|
||||
|
||||
Suite raii = [] {
|
||||
Suite raii = "raii"_suite = "raii"_suite = [] {
|
||||
Case { "happy path won't throw" } = [&] {
|
||||
System { Fixture {}.registry() };
|
||||
};
|
||||
|
|
@ -76,7 +76,7 @@ Suite raii = [] {
|
|||
};
|
||||
};
|
||||
|
||||
Suite system_events = [] {
|
||||
Suite system_events = "system_events"_suite = [] {
|
||||
Case { "on_register won't throw" } = [] {
|
||||
auto fixture = Fixture {};
|
||||
auto registry = fixture.registry();
|
||||
|
|
@ -97,7 +97,7 @@ Suite system_events = [] {
|
|||
};
|
||||
};
|
||||
|
||||
Suite registry_events = [] {
|
||||
Suite registry_events = "registry_events"_suite = [] {
|
||||
Case { "on_construct<InputComnent>" } = [] {
|
||||
auto fixture = Fixture {};
|
||||
auto registry = fixture.registry();
|
||||
|
|
@ -127,7 +127,7 @@ Suite registry_events = [] {
|
|||
};
|
||||
};
|
||||
|
||||
Suite tick = [] {
|
||||
Suite tick = "tick"_suite = [] {
|
||||
Case { "Empty tick won't throw" } = [] {
|
||||
auto fixture = Fixture {};
|
||||
auto registry = fixture.registry();
|
||||
|
|
|
|||
|
|
@ -1,15 +1,37 @@
|
|||
add_library_module(renderer
|
||||
system.cpp
|
||||
vk/context.cpp
|
||||
vk/debug/messenger.cpp
|
||||
vk/context/instance.cpp
|
||||
vk/context/surface.cpp
|
||||
vk/context/device.cpp
|
||||
vk/context/swapchain.cpp
|
||||
vk/context/context.cpp
|
||||
vk/pipeline.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(renderer
|
||||
PUBLIC
|
||||
app
|
||||
ecs
|
||||
memory
|
||||
PRIVATE
|
||||
surface
|
||||
pthread
|
||||
)
|
||||
|
||||
add_test_module(renderer
|
||||
system.test.cpp
|
||||
vk/test_utils.cpp
|
||||
vk/debug/messenger.test.cpp
|
||||
vk/context/instance.test.cpp
|
||||
vk/context/surface.test.cpp
|
||||
vk/context/device.test.cpp
|
||||
vk/context/swapchain.test.cpp
|
||||
vk/context/context.test.cpp
|
||||
vk/pipeline.test.cpp
|
||||
)
|
||||
target_link_libraries(renderer_tests
|
||||
PRIVATE
|
||||
surface
|
||||
pthread
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
#include <ranges>
|
||||
#include <renderer/system.hpp>
|
||||
#include <test/test.hpp>
|
||||
|
||||
using namespace lt;
|
||||
#include <renderer/backend.hpp>
|
||||
|
||||
using lt::test::Case;
|
||||
using lt::test::Suite;
|
||||
|
||||
Suite raii = [] {
|
||||
Case { "happy path won't throw" } = [] {
|
||||
};
|
||||
};
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
#include <renderer/blender.hpp>
|
||||
#include <renderer/gl/blender.hpp>
|
||||
|
||||
#ifdef LIGHT_PLATFORM_WINDOWS
|
||||
#include <renderer/dx/blender.hpp>
|
||||
#include <renderer/dx/sharedcontext.hpp>
|
||||
#endif
|
||||
|
||||
#include <renderer/graphics_context.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
auto Blender::create(const Ref<SharedContext> & /*sharedContext*/) -> Scope<Blender>
|
||||
{
|
||||
switch (GraphicsContext::get_graphics_api())
|
||||
{
|
||||
case GraphicsAPI::OpenGL: return create_scope<glBlender>();
|
||||
|
||||
case GraphicsAPI::DirectX:
|
||||
lt_win(
|
||||
return create_scope<dxBlender>(
|
||||
std::static_pointer_cast<dxSharedContext>(sharedContext)
|
||||
);
|
||||
)
|
||||
|
||||
default
|
||||
: ensure(
|
||||
false,
|
||||
"Invalid/unsupported 'GraphicsAPI' {}",
|
||||
static_cast<uint32_t>(GraphicsContext::get_graphics_api())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
#include <lt_debug/assertions.hpp>
|
||||
#include <renderer/buffers.hpp>
|
||||
#include <renderer/gl/buffers.hpp>
|
||||
#include <renderer/shared_context.hpp>
|
||||
|
||||
#ifdef LIGHT_PLATFORM_WINDOWS
|
||||
#include <renderer/dx/buffers.hpp>
|
||||
#include <renderer/dx/shared_context.hpp>
|
||||
#endif
|
||||
|
||||
#include <renderer/graphics_context.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
auto ConstantBuffer::create(
|
||||
ConstantBufferIndex index,
|
||||
unsigned int size,
|
||||
const Ref<SharedContext> & /*sharedContext*/
|
||||
) -> Scope<ConstantBuffer>
|
||||
{
|
||||
switch (GraphicsContext::get_graphics_api())
|
||||
{
|
||||
case GraphicsAPI::OpenGL: return create_scope<glConstantBuffer>(index, size);
|
||||
|
||||
case GraphicsAPI::DirectX:
|
||||
lt_win(
|
||||
return create_scope<dxConstantBuffer>(
|
||||
index,
|
||||
size,
|
||||
std::static_pointer_cast<dxSharedContext>(sharedContext)
|
||||
);
|
||||
)
|
||||
|
||||
default
|
||||
: ensure(
|
||||
false,
|
||||
"Invalid/unsupported 'GraphicsAPI' {}",
|
||||
static_cast<uint32_t>(GraphicsContext::get_graphics_api())
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto VertexBuffer::create(
|
||||
float *vertices,
|
||||
unsigned int stride,
|
||||
unsigned int count,
|
||||
const Ref<SharedContext> & /*sharedContext*/
|
||||
) -> Ref<VertexBuffer>
|
||||
{
|
||||
switch (GraphicsContext::get_graphics_api())
|
||||
{
|
||||
case GraphicsAPI::OpenGL: return create_ref<glVertexBuffer>(vertices, stride, count);
|
||||
|
||||
case GraphicsAPI::DirectX:
|
||||
lt_win(
|
||||
return create_ref<dxVertexBuffer>(
|
||||
vertices,
|
||||
stride,
|
||||
count,
|
||||
std::static_pointer_cast<dxSharedContext>(sharedContext)
|
||||
);
|
||||
)
|
||||
|
||||
default
|
||||
: ensure(
|
||||
false,
|
||||
"Invalid/unsupported 'GraphicsAPI' {}",
|
||||
static_cast<uint32_t>(GraphicsContext::get_graphics_api())
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto IndexBuffer::create(
|
||||
unsigned int *indices,
|
||||
unsigned int count,
|
||||
const Ref<SharedContext> & /*sharedContext*/
|
||||
) -> Ref<IndexBuffer>
|
||||
{
|
||||
switch (GraphicsContext::get_graphics_api())
|
||||
{
|
||||
case GraphicsAPI::OpenGL: return create_ref<glIndexBuffer>(indices, count);
|
||||
|
||||
case GraphicsAPI::DirectX:
|
||||
lt_win(
|
||||
return create_ref<dxIndexBuffer>(
|
||||
indices,
|
||||
count,
|
||||
std::dynamic_pointer_cast<dxSharedContext>(sharedContext)
|
||||
);
|
||||
)
|
||||
|
||||
default
|
||||
: ensure(
|
||||
false,
|
||||
"Invalid/unsupported 'GraphicsAPI' {}",
|
||||
static_cast<uint32_t>(GraphicsContext::get_graphics_api())
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
#include <renderer/dx/blender.hpp>
|
||||
#include <renderer/dx/shared_context.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
dxBlender::dxBlender(Ref<dxSharedContext> sharedContext)
|
||||
: m_context(sharedContext), m_factor_map { // constants
|
||||
{ BlendFactor::ZERO, D3D11_BLEND_ZERO },
|
||||
{ BlendFactor::ONE, D3D11_BLEND_ONE },
|
||||
|
||||
// source
|
||||
{ BlendFactor::SRC_COLOR, D3D11_BLEND_SRC_COLOR },
|
||||
{ BlendFactor::INVERSE_SRC_COLOR, D3D11_BLEND_INV_SRC_COLOR },
|
||||
|
||||
{ BlendFactor::SRC_ALPHA, D3D11_BLEND_SRC_ALPHA },
|
||||
{ BlendFactor::INVERSE_SRC_ALPHA, D3D11_BLEND_INV_SRC_ALPHA },
|
||||
|
||||
// destination
|
||||
{ BlendFactor::DST_COLOR, D3D11_BLEND_DEST_COLOR },
|
||||
{ BlendFactor::INVERSE_DST_COLOR, D3D11_BLEND_INV_DEST_COLOR },
|
||||
|
||||
{ BlendFactor::DST_ALPHA, D3D11_BLEND_DEST_ALPHA },
|
||||
{ BlendFactor::INVERSE_DST_ALPHA, D3D11_BLEND_INV_DEST_ALPHA },
|
||||
|
||||
// source1
|
||||
{ BlendFactor::SRC1_COLOR, D3D11_BLEND_SRC1_COLOR },
|
||||
{ BlendFactor::INVERSE_SRC1_COLOR, D3D11_BLEND_INV_SRC1_COLOR },
|
||||
|
||||
{ BlendFactor::SRC1_ALPHA, D3D11_BLEND_SRC1_ALPHA },
|
||||
{ BlendFactor::INVERSE_SRC1_ALPHA, D3D11_BLEND_INV_SRC1_ALPHA }
|
||||
}
|
||||
, m_blend_state(nullptr)
|
||||
, m_desc {}
|
||||
{
|
||||
// factor map
|
||||
// blender desc
|
||||
m_desc = {};
|
||||
|
||||
m_desc.RenderTarget[0].BlendEnable = true;
|
||||
m_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ZERO;
|
||||
m_desc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO;
|
||||
m_desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
|
||||
m_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ZERO;
|
||||
m_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
|
||||
m_desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
|
||||
m_desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
|
||||
|
||||
// create blend state
|
||||
auto hr = HRESULT {};
|
||||
dxc(m_context->get_device()->CreateBlendState(&m_desc, &m_blend_state));
|
||||
}
|
||||
|
||||
void dxBlender::enable(BlendFactor srcFactor, BlendFactor dstFactor)
|
||||
{
|
||||
// update desc
|
||||
m_desc.RenderTarget[0].BlendEnable = true;
|
||||
m_desc.RenderTarget[0].SrcBlend = m_factor_map.at(srcFactor);
|
||||
m_desc.RenderTarget[0].DestBlend = m_factor_map.at(dstFactor);
|
||||
|
||||
// re-create blind state
|
||||
auto hr = HRESULT {};
|
||||
dxc(m_context->get_device()->CreateBlendState(&m_desc, &m_blend_state));
|
||||
|
||||
// bind blend state
|
||||
m_context->get_device_context()->OMSetBlendState(m_blend_state.Get(), nullptr, 0x0000000f);
|
||||
}
|
||||
|
||||
void dxBlender::disable()
|
||||
{
|
||||
// update desc
|
||||
m_desc.RenderTarget[0].BlendEnable = false;
|
||||
|
||||
// re-create blind state
|
||||
auto hr = HRESULT {};
|
||||
dxc(m_context->get_device()->CreateBlendState(&m_desc, &m_blend_state));
|
||||
|
||||
// bind blend state
|
||||
m_context->get_device_context()->OMSetBlendState(m_blend_state.Get(), nullptr, 0xffffffff);
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <renderer/blender.hpp>
|
||||
#include <wrl.h>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class dxSharedContext;
|
||||
|
||||
class dxBlender: public Blender
|
||||
{
|
||||
public:
|
||||
dxBlender(Ref<dxSharedContext> sharedContext);
|
||||
|
||||
void enable(BlendFactor srcFactor, BlendFactor dstFactor) override;
|
||||
|
||||
void disable() override;
|
||||
|
||||
private:
|
||||
Ref<dxSharedContext> m_context;
|
||||
|
||||
const std::unordered_map<BlendFactor, D3D11_BLEND> m_factor_map;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D11BlendState> m_blend_state;
|
||||
|
||||
D3D11_BLEND_DESC m_desc;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,188 +0,0 @@
|
|||
#include <renderer/dx/buffers.hpp>
|
||||
#include <renderer/dx/shared_context.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
//======================================== CONSTANT_BUFFER
|
||||
//========================================//
|
||||
dxConstantBuffer::dxConstantBuffer(
|
||||
ConstantBufferIndex index,
|
||||
unsigned int size,
|
||||
Ref<dxSharedContext> sharedContext
|
||||
)
|
||||
: m_context(sharedContext)
|
||||
, m_buffer(nullptr)
|
||||
, m_map {}
|
||||
, m_index(static_cast<int>(index))
|
||||
{
|
||||
auto bDesc = D3D11_BUFFER_DESC {};
|
||||
|
||||
bDesc.ByteWidth = size;
|
||||
bDesc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
bDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
|
||||
bDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
|
||||
auto hr = HRESULT {};
|
||||
dxc(m_context->get_device()->CreateBuffer(&bDesc, nullptr, &m_buffer));
|
||||
m_context->get_device_context()->VSSetConstantBuffers(m_index, 1u, m_buffer.GetAddressOf());
|
||||
}
|
||||
|
||||
void dxConstantBuffer::bind()
|
||||
{
|
||||
m_context->get_device_context()->VSSetConstantBuffers(m_index, 1u, m_buffer.GetAddressOf());
|
||||
}
|
||||
|
||||
auto dxConstantBuffer::map() -> void *
|
||||
{
|
||||
m_context->get_device_context()->VSSetConstantBuffers(m_index, 1u, m_buffer.GetAddressOf());
|
||||
m_context->get_device_context()
|
||||
->map(m_buffer.Get(), NULL, D3D11_MAP_WRITE_DISCARD, NULL, &m_map);
|
||||
return m_map.pData;
|
||||
}
|
||||
|
||||
void dxConstantBuffer::un_map()
|
||||
{
|
||||
m_context->get_device_context()->Unmap(m_buffer.Get(), NULL);
|
||||
}
|
||||
|
||||
dxVertexBuffer::dxVertexBuffer(
|
||||
float *vertices,
|
||||
unsigned int stride,
|
||||
unsigned int count,
|
||||
Ref<dxSharedContext> sharedContext
|
||||
)
|
||||
: m_context(sharedContext)
|
||||
, m_buffer(nullptr)
|
||||
, m_map {}
|
||||
, m_stride(stride)
|
||||
{
|
||||
// buffer desc
|
||||
auto bDesc = D3D11_BUFFER_DESC {};
|
||||
|
||||
bDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
|
||||
bDesc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
bDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
|
||||
bDesc.ByteWidth = count * stride;
|
||||
bDesc.StructureByteStride = stride;
|
||||
|
||||
// create buffer
|
||||
auto hr = HRESULT {};
|
||||
dxc(m_context->get_device()->CreateBuffer(&bDesc, nullptr, &m_buffer));
|
||||
}
|
||||
|
||||
dxVertexBuffer::~dxVertexBuffer()
|
||||
{
|
||||
un_bind();
|
||||
}
|
||||
|
||||
auto dxVertexBuffer::map() -> void *
|
||||
{
|
||||
m_context->get_device_context()
|
||||
->map(m_buffer.Get(), NULL, D3D11_MAP_WRITE_DISCARD, NULL, &m_map);
|
||||
return m_map.pData;
|
||||
}
|
||||
|
||||
void dxVertexBuffer::un_map()
|
||||
{
|
||||
m_context->get_device_context()->Unmap(m_buffer.Get(), NULL);
|
||||
}
|
||||
|
||||
void dxVertexBuffer::bind()
|
||||
{
|
||||
static const auto offset = unsigned int { 0u };
|
||||
m_context->get_device_context()
|
||||
->IASetVertexBuffers(0u, 1u, m_buffer.GetAddressOf(), &m_stride, &offset);
|
||||
}
|
||||
|
||||
void dxVertexBuffer::un_bind()
|
||||
{
|
||||
static const auto offset = unsigned int { 0u };
|
||||
static auto *buffer = (ID3D11Buffer *)(nullptr);
|
||||
|
||||
m_context->get_device_context()->IASetVertexBuffers(0u, 1u, &buffer, &m_stride, &offset);
|
||||
}
|
||||
|
||||
dxIndexBuffer::dxIndexBuffer(
|
||||
unsigned int *indices,
|
||||
unsigned int count,
|
||||
Ref<dxSharedContext> sharedContext
|
||||
)
|
||||
: m_context(sharedContext)
|
||||
, m_buffer(nullptr)
|
||||
{
|
||||
// generate indices if not provided
|
||||
auto hasIndices = !!indices;
|
||||
if (!hasIndices)
|
||||
{
|
||||
// check
|
||||
if (count % 6 != 0)
|
||||
{
|
||||
log_wrn("'indices' can only be null if count is multiple of 6");
|
||||
lt_log(
|
||||
warn,
|
||||
"Adding {} to 'count' -> {}",
|
||||
(6 - (count % 6)),
|
||||
count + (6 - (count % 6))
|
||||
);
|
||||
count = count + (6 - (count % 6));
|
||||
}
|
||||
|
||||
// create indices
|
||||
indices = new unsigned int[count];
|
||||
auto offset = 0;
|
||||
for (unsigned int i = 0; i < count; i += 6)
|
||||
{
|
||||
indices[i + 0] = offset + 0u;
|
||||
indices[i + 1] = offset + 3u;
|
||||
indices[i + 2] = offset + 2u;
|
||||
|
||||
indices[i + 3] = offset + 2u;
|
||||
indices[i + 4] = offset + 1u;
|
||||
indices[i + 5] = offset + 0u;
|
||||
|
||||
offset += 4u;
|
||||
}
|
||||
}
|
||||
|
||||
// buffer desc
|
||||
auto bDesc = D3D11_BUFFER_DESC {};
|
||||
bDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
|
||||
bDesc.Usage = D3D11_USAGE_DEFAULT;
|
||||
|
||||
bDesc.ByteWidth = count * sizeof(unsigned int);
|
||||
bDesc.StructureByteStride = sizeof(unsigned int);
|
||||
|
||||
// subresource data
|
||||
auto sDesc = D3D11_SUBRESOURCE_DATA {};
|
||||
sDesc.pSysMem = indices;
|
||||
|
||||
// create buffer
|
||||
auto hr = HRESULT {};
|
||||
dxc(m_context->get_device()->CreateBuffer(&bDesc, &sDesc, &m_buffer));
|
||||
|
||||
// delete indices
|
||||
if (!hasIndices)
|
||||
delete[] indices;
|
||||
}
|
||||
|
||||
dxIndexBuffer::~dxIndexBuffer()
|
||||
{
|
||||
un_bind();
|
||||
}
|
||||
|
||||
void dxIndexBuffer::bind()
|
||||
{
|
||||
m_context->get_device_context()->IASetIndexBuffer(m_buffer.Get(), DXGI_FORMAT_R32_UINT, 0u);
|
||||
}
|
||||
|
||||
void dxIndexBuffer::un_bind()
|
||||
{
|
||||
static const auto offset = (unsigned int) { 0u };
|
||||
static auto *buffer = (ID3D11Buffer *)(nullptr);
|
||||
|
||||
m_context->get_device_context()->IASetIndexBuffer(buffer, DXGI_FORMAT_R32_UINT, offset);
|
||||
}
|
||||
//======================================== INDEX_BUFFER ========================================//
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <renderer/buffers.hpp>
|
||||
#include <wrl.h>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class dxSharedContext;
|
||||
|
||||
class dxConstantBuffer: public ConstantBuffer
|
||||
{
|
||||
public:
|
||||
dxConstantBuffer(
|
||||
ConstantBufferIndex index,
|
||||
unsigned int size,
|
||||
Ref<dxSharedContext> sharedContext
|
||||
);
|
||||
|
||||
void bind() override;
|
||||
|
||||
void *map() override;
|
||||
|
||||
void un_map() override;
|
||||
|
||||
private:
|
||||
Ref<dxSharedContext> m_context;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D11Buffer> m_buffer;
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE m_map;
|
||||
|
||||
unsigned int m_index;
|
||||
};
|
||||
|
||||
class dxVertexBuffer: public VertexBuffer
|
||||
{
|
||||
public:
|
||||
dxVertexBuffer(
|
||||
float *vertices,
|
||||
unsigned int stride,
|
||||
unsigned int count,
|
||||
Ref<dxSharedContext> sharedContext
|
||||
);
|
||||
|
||||
~dxVertexBuffer();
|
||||
|
||||
void bind() override;
|
||||
|
||||
void un_bind() override;
|
||||
|
||||
auto map() -> void * override;
|
||||
|
||||
void un_map() override;
|
||||
|
||||
private:
|
||||
Ref<dxSharedContext> m_context;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D11Buffer> m_buffer;
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE m_map;
|
||||
|
||||
unsigned int m_stride;
|
||||
};
|
||||
|
||||
class dxIndexBuffer: public IndexBuffer
|
||||
{
|
||||
public:
|
||||
dxIndexBuffer(unsigned int *indices, unsigned int count, Ref<dxSharedContext> sharedContext);
|
||||
|
||||
~dxIndexBuffer();
|
||||
|
||||
void bind() override;
|
||||
|
||||
void un_bind() override;
|
||||
|
||||
private:
|
||||
Ref<dxSharedContext> m_context;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D11Buffer> m_buffer;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
#include <renderer/dx/framebuffers.hpp>
|
||||
#include <renderer/dx/shared_context.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
dxFramebuffer::dxFramebuffer(
|
||||
const FramebufferSpecification &specification,
|
||||
Ref<dxSharedContext> sharedContext
|
||||
)
|
||||
: m_context(sharedContext)
|
||||
, m_specification(specification)
|
||||
, m_render_target_view(nullptr)
|
||||
, m_color_attachment(nullptr)
|
||||
, m_depth_stencil_attachment(nullptr)
|
||||
, m_shader_resource_view(nullptr)
|
||||
, m_depth_stencil_view(nullptr)
|
||||
{
|
||||
auto hr = HRESULT {};
|
||||
|
||||
auto t2dDesc = D3D11_TEXTURE2D_DESC {};
|
||||
t2dDesc.Width = specification.width;
|
||||
t2dDesc.Height = specification.height;
|
||||
t2dDesc.MipLevels = 1;
|
||||
t2dDesc.ArraySize = 1;
|
||||
t2dDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
t2dDesc.SampleDesc.Count = 1u;
|
||||
t2dDesc.SampleDesc.Quality = 0u;
|
||||
t2dDesc.Usage = D3D11_USAGE_DEFAULT;
|
||||
t2dDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
|
||||
t2dDesc.CPUAccessFlags = NULL;
|
||||
t2dDesc.MiscFlags = NULL;
|
||||
dxc(m_context->get_device()->CreateTexture2D(&t2dDesc, nullptr, &m_color_attachment));
|
||||
|
||||
auto srvDesc = D3D11_SHADER_RESOURCE_VIEW_DESC {};
|
||||
srvDesc.Format = t2dDesc.Format;
|
||||
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
||||
srvDesc.Texture2D.MipLevels = 1;
|
||||
srvDesc.Texture2D.MostDetailedMip = 0;
|
||||
dxc(m_context->get_device()->CreateShaderResourceView(
|
||||
m_color_attachment.Get(),
|
||||
&srvDesc,
|
||||
&m_shader_resource_view
|
||||
));
|
||||
|
||||
auto rtvDesc = D3D11_RENDER_TARGET_VIEW_DESC {};
|
||||
rtvDesc.Format = t2dDesc.Format;
|
||||
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
|
||||
rtvDesc.Texture2D.MipSlice = 0u;
|
||||
dxc(m_context->get_device()
|
||||
->CreateRenderTargetView(m_color_attachment.Get(), &rtvDesc, &m_render_target_view));
|
||||
}
|
||||
|
||||
void dxFramebuffer::bind_as_target(const glm::vec4 &clearColor)
|
||||
{
|
||||
FLOAT color[] = {
|
||||
clearColor.r,
|
||||
clearColor.g,
|
||||
clearColor.b,
|
||||
clearColor.a,
|
||||
};
|
||||
|
||||
m_context->get_device_context()
|
||||
->OMSetRenderTargets(1u, m_render_target_view.GetAddressOf(), nullptr);
|
||||
m_context->get_device_context()->ClearRenderTargetView(m_render_target_view.Get(), color);
|
||||
|
||||
auto viewport = D3D11_VIEWPORT {};
|
||||
|
||||
viewport.TopLeftX = 0;
|
||||
viewport.TopLeftY = 0;
|
||||
|
||||
viewport.Width = m_specification.width;
|
||||
viewport.Height = m_specification.height;
|
||||
|
||||
viewport.MinDepth = 0.0f;
|
||||
viewport.MaxDepth = 1.0f;
|
||||
|
||||
// set viewport
|
||||
m_context->get_device_context()->RSSetViewports(1u, &viewport);
|
||||
}
|
||||
|
||||
void dxFramebuffer::bind_as_resource()
|
||||
{
|
||||
log_err("NO_IMPLEMENT");
|
||||
}
|
||||
|
||||
void dxFramebuffer::resize(const glm::uvec2 &size)
|
||||
{
|
||||
m_specification.width = std::clamp(size.x, 1u, 16384u);
|
||||
m_specification.height = std::clamp(size.y, 1u, 16384u);
|
||||
|
||||
auto textureDesc = D3D11_TEXTURE2D_DESC {};
|
||||
auto rtvDesc = D3D11_RENDER_TARGET_VIEW_DESC {};
|
||||
auto srvDesc = D3D11_SHADER_RESOURCE_VIEW_DESC {};
|
||||
|
||||
m_color_attachment->GetDesc(&textureDesc);
|
||||
m_render_target_view->GetDesc(&rtvDesc);
|
||||
m_shader_resource_view->GetDesc(&srvDesc);
|
||||
|
||||
textureDesc.Width = m_specification.width;
|
||||
textureDesc.Height = m_specification.height;
|
||||
|
||||
auto hr = HRESULT {};
|
||||
dxc(m_context->get_device()->CreateTexture2D(&textureDesc, nullptr, &m_color_attachment));
|
||||
dxc(m_context->get_device()
|
||||
->CreateRenderTargetView(m_color_attachment.Get(), &rtvDesc, &m_render_target_view));
|
||||
dxc(m_context->get_device()->CreateShaderResourceView(
|
||||
m_color_attachment.Get(),
|
||||
&srvDesc,
|
||||
&m_shader_resource_view
|
||||
));
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <renderer/framebuffer.hpp>
|
||||
#include <wrl.h>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class dxSharedContext;
|
||||
|
||||
class dxFramebuffer: public Framebuffer
|
||||
{
|
||||
public:
|
||||
dxFramebuffer(
|
||||
const FramebufferSpecification &specification,
|
||||
Ref<dxSharedContext> sharedContext
|
||||
);
|
||||
|
||||
auto get_color_attachment() -> void * override
|
||||
{
|
||||
return (void *)m_shader_resource_view.Get();
|
||||
}
|
||||
|
||||
void bind_as_target(const glm::vec4 &clearColor) override;
|
||||
|
||||
void bind_as_resource() override;
|
||||
|
||||
void resize(const glm::uvec2 &size) override;
|
||||
|
||||
private:
|
||||
Ref<dxSharedContext> m_context;
|
||||
|
||||
FramebufferSpecification m_specification;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D11RenderTargetView> m_render_target_view;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D11Texture2D> m_color_attachment;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D11Texture2D> m_depth_stencil_attachment;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> m_shader_resource_view;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D11DepthStencilView> m_depth_stencil_view;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,159 +0,0 @@
|
|||
#include <input/events/window.hpp>
|
||||
#include <renderer/blender.hpp" // required for forward declaratio>
|
||||
#include <renderer/buffers.hpp" // required for forward declaratio>
|
||||
#include <renderer/dx/graphics_context.hpp>
|
||||
#include <renderer/dx/shared_context.hpp>
|
||||
#include <renderer/render_command.hpp> // required for forward declaratio>
|
||||
#include <renderer/renderer.hpp> // required for forward declaratio>
|
||||
#include <ui/ui.hpp> // required for forward declaratio>
|
||||
|
||||
namespace lt {
|
||||
|
||||
dxGraphicsContext::dxGraphicsContext(): m_window_handle(windowHandle), m_debug_interface(nullptr)
|
||||
{
|
||||
// set 'GraphicsAPI';
|
||||
m_graphics_api = GraphicsAPI::DirectX;
|
||||
|
||||
m_shared_context = std::make_shared<dxSharedContext>();
|
||||
|
||||
// setup stuff
|
||||
setup_device_and_swap_chain(windowHandle);
|
||||
setup_render_targets();
|
||||
setup_debug_interface();
|
||||
}
|
||||
|
||||
void dxGraphicsContext::setup_device_and_swap_chain()
|
||||
{
|
||||
auto context = std::static_pointer_cast<dxSharedContext>(m_shared_context);
|
||||
|
||||
// swap chain desc
|
||||
auto sd = DXGI_SWAP_CHAIN_DESC { 0 };
|
||||
|
||||
// buffer desc
|
||||
sd.BufferDesc.Width = 1u;
|
||||
sd.BufferDesc.Height = 1u;
|
||||
sd.BufferDesc.RefreshRate.Numerator = NULL; // :#todo
|
||||
sd.BufferDesc.RefreshRate.Denominator = NULL; // :#todo
|
||||
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
|
||||
sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
|
||||
|
||||
// sample desc (for multi sampling) #todo: implement multi-samplingz
|
||||
sd.SampleDesc.Count = 1u;
|
||||
sd.SampleDesc.Quality = 0u;
|
||||
|
||||
// #todo: support swap chains with more than 1 back-buffer
|
||||
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
sd.BufferCount = 1u;
|
||||
|
||||
sd.OutputWindow = {}; // ...
|
||||
sd.Windowed = true;
|
||||
|
||||
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
|
||||
|
||||
sd.Flags = NULL;
|
||||
|
||||
// determine device flags
|
||||
auto flags = UINT { NULL };
|
||||
#ifdef LIGHT_DEBUG
|
||||
flags = D3D11_CREATE_DEVICE_DEBUG;
|
||||
#endif
|
||||
|
||||
// create device and swap chain
|
||||
dxc(D3D11CreateDeviceAndSwapChain(
|
||||
nullptr,
|
||||
D3D_DRIVER_TYPE_HARDWARE,
|
||||
NULL,
|
||||
flags,
|
||||
nullptr,
|
||||
NULL,
|
||||
D3D11_SDK_VERSION,
|
||||
&sd,
|
||||
&context->GetSwapChainRef(),
|
||||
&context->GetDeviceRef(),
|
||||
nullptr,
|
||||
&context->GetDeviceContextRef()
|
||||
));
|
||||
}
|
||||
|
||||
void dxGraphicsContext::setup_render_targets()
|
||||
{
|
||||
auto context = std::static_pointer_cast<dxSharedContext>(m_shared_context);
|
||||
|
||||
// set primitive topology
|
||||
context->get_device_context()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
|
||||
// create render target view
|
||||
auto backBuffer = Microsoft::WRL::ComPtr<ID3D11Resource> {};
|
||||
|
||||
dxc(context->get_swap_chain()->GetBuffer(0u, __uuidof(ID3D11Resource), &backBuffer));
|
||||
dxc(context->get_device()->CreateRenderTargetView(
|
||||
backBuffer.Get(),
|
||||
nullptr,
|
||||
&context->GetRenderTargetViewRef()
|
||||
));
|
||||
|
||||
// set render target view
|
||||
context->get_device_context()
|
||||
->OMSetRenderTargets(1u, context->get_render_target_view().GetAddressOf(), nullptr);
|
||||
}
|
||||
|
||||
void dxGraphicsContext::setup_debug_interface()
|
||||
{
|
||||
#ifdef LIGHT_DEBUG
|
||||
Ref<dxSharedContext> context = std::static_pointer_cast<dxSharedContext>(m_shared_context);
|
||||
|
||||
HRESULT hr;
|
||||
Microsoft::WRL::ComPtr<ID3D11Debug> debugInterface = nullptr;
|
||||
dxc(context->get_device()->QueryInterface(__uuidof(ID3D11Debug), &debugInterface));
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D11InfoQueue> infoQueue = nullptr;
|
||||
dxc(debugInterface->QueryInterface(__uuidof(ID3D11InfoQueue), &infoQueue));
|
||||
|
||||
infoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, true);
|
||||
infoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, true);
|
||||
|
||||
D3D11_MESSAGE_ID hide[] = {
|
||||
D3D11_MESSAGE_ID_UNKNOWN,
|
||||
// #todo: add more messages here as needed
|
||||
};
|
||||
|
||||
D3D11_INFO_QUEUE_FILTER filter = {};
|
||||
filter.DenyList.NumIDs = _countof(hide);
|
||||
filter.DenyList.pIDList = hide;
|
||||
infoQueue->AddStorageFilterEntries(&filter);
|
||||
infoQueue->release();
|
||||
#endif
|
||||
}
|
||||
|
||||
void dxGraphicsContext::log_debug_data()
|
||||
{
|
||||
auto context = std::static_pointer_cast<dxSharedContext>(m_shared_context);
|
||||
|
||||
// locals
|
||||
auto *DXGIDevice = (IDXGIDevice *) {};
|
||||
auto *DXGIAdapter = (IDXGIAdapter *) {};
|
||||
auto *DXGIAdapterDesc = (DXGI_ADAPTER_DESC *) {};
|
||||
|
||||
context->get_device()->QueryInterface(__uuidof(IDXGIDevice), (void **)&DXGIDevice);
|
||||
DXGIDevice->GetAdapter(&DXGIAdapter);
|
||||
DXGIAdapter->GetDesc(&DXGIAdapterDesc);
|
||||
|
||||
// get the adapter's description
|
||||
auto DefChar = ' ';
|
||||
char ch[180];
|
||||
WideCharToMultiByte(CP_ACP, 0, DXGIAdapterDesc.Description, -1, ch, 180, &DefChar, NULL);
|
||||
auto adapterDesc = std::string { ch };
|
||||
|
||||
// release memory
|
||||
DXGIDevice->release();
|
||||
DXGIAdapter->release();
|
||||
|
||||
// #todo: log more information
|
||||
log_inf("________________________________________");
|
||||
log_inf("dxGraphicsContext:");
|
||||
log_inf(" renderer: {}", adapterDesc);
|
||||
log_inf("________________________________________");
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <renderer/graphics_context.hpp>
|
||||
#include <wrl.h>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class dxGraphicsContext: public GraphicsContext
|
||||
{
|
||||
public:
|
||||
dxGraphicsContext();
|
||||
|
||||
virtual void log_debug_data() override;
|
||||
|
||||
private:
|
||||
Microsoft::WRL::ComPtr<ID3D11Debug> m_debug_interface;
|
||||
|
||||
void setup_device_and_swap_chain();
|
||||
|
||||
void setup_render_targets();
|
||||
|
||||
void setup_debug_interface();
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
#include <renderer/dx/render_command.hpp>
|
||||
#include <renderer/dx/shared_context.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
dxRenderCommand::dxRenderCommand(Ref<dxSharedContext> sharedContext): m_context(sharedContext)
|
||||
{
|
||||
}
|
||||
|
||||
void dxRenderCommand::swap_buffers()
|
||||
{
|
||||
#ifdef LIGHT_DEBUG
|
||||
HRESULT hr;
|
||||
if (FAILED(hr = m_context->get_swap_chain()->Present(1u, 0u)))
|
||||
{
|
||||
if (hr == DXGI_ERROR_DEVICE_REMOVED)
|
||||
{
|
||||
log_crt("dxRenderCommand::swap_buffers: DeviceRemoved:");
|
||||
log_crt(" {}", m_context->get_device()->GetDeviceRemovedReason());
|
||||
throw dxException(hr, __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
#else
|
||||
m_context->get_swap_chain()->Present(0u, 0u);
|
||||
#endif
|
||||
}
|
||||
|
||||
void dxRenderCommand::clear_back_buffer(const glm::vec4 &clearColor)
|
||||
{
|
||||
m_context->get_device_context()->ClearRenderTargetView(
|
||||
m_context->get_render_target_view().Get(),
|
||||
&clearColor[0]
|
||||
);
|
||||
}
|
||||
|
||||
void dxRenderCommand::draw(unsigned int count)
|
||||
{
|
||||
m_context->get_device_context()->draw(count, 0u);
|
||||
}
|
||||
|
||||
void dxRenderCommand::draw_indexed(unsigned int count)
|
||||
{
|
||||
m_context->get_device_context()->draw_indexed(count, 0u, 0u);
|
||||
}
|
||||
|
||||
void dxRenderCommand::default_target_framebuffer()
|
||||
{
|
||||
m_context->get_device_context()
|
||||
->OMSetRenderTargets(1, m_context->get_render_target_view().GetAddressOf(), nullptr);
|
||||
}
|
||||
|
||||
void dxRenderCommand::set_viewport(
|
||||
unsigned int x,
|
||||
unsigned int y,
|
||||
unsigned int width,
|
||||
unsigned int height
|
||||
)
|
||||
{
|
||||
// #todo: maybe call this somewhere else??
|
||||
set_resolution(width, height);
|
||||
|
||||
// create viewport
|
||||
auto viewport = D3D11_VIEWPORT {};
|
||||
|
||||
viewport.TopLeftX = x;
|
||||
viewport.TopLeftY = y;
|
||||
|
||||
viewport.Width = width;
|
||||
viewport.Height = height;
|
||||
|
||||
viewport.MinDepth = 0.0f;
|
||||
viewport.MaxDepth = 1.0f;
|
||||
|
||||
// set viewport
|
||||
m_context->get_device_context()->RSSetViewports(1u, &viewport);
|
||||
}
|
||||
|
||||
void dxRenderCommand::set_resolution(unsigned int width, unsigned int height)
|
||||
{
|
||||
auto hr = HRESULT {};
|
||||
|
||||
// remove render target
|
||||
auto *nullViews[] = (ID3D11RenderTargetView *) { nullptr };
|
||||
m_context->get_device_context()->OMSetRenderTargets(1u, nullViews, nullptr);
|
||||
m_context->GetRenderTargetViewRef().reset();
|
||||
|
||||
// resize buffer
|
||||
dxc(m_context->get_swap_chain()
|
||||
->ResizeBuffers(0u, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, NULL));
|
||||
|
||||
// create render target
|
||||
auto backBuffer = Microsoft::WRL::ComPtr<ID3D11Resource> { nullptr };
|
||||
dxc(m_context->get_swap_chain()->GetBuffer(0u, __uuidof(ID3D11Resource), &backBuffer));
|
||||
dxc(m_context->get_device()->CreateRenderTargetView(
|
||||
backBuffer.Get(),
|
||||
nullptr,
|
||||
&m_context->GetRenderTargetViewRef()
|
||||
));
|
||||
|
||||
// set render target
|
||||
m_context->get_device_context()
|
||||
->OMSetRenderTargets(1u, m_context->get_render_target_view().GetAddressOf(), nullptr);
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <renderer/render_command.hpp>
|
||||
#include <wrl.h>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class dxSharedContext;
|
||||
|
||||
class dxRenderCommand: public RenderCommand
|
||||
{
|
||||
public:
|
||||
dxRenderCommand(Ref<dxSharedContext> sharedContext);
|
||||
|
||||
virtual void swap_buffers() override;
|
||||
|
||||
virtual void clear_back_buffer(const glm::vec4 &clearColor) override;
|
||||
|
||||
virtual void draw(unsigned int count) override;
|
||||
|
||||
virtual void draw_indexed(unsigned int count) override;
|
||||
|
||||
virtual void default_target_framebuffer() override;
|
||||
|
||||
virtual void set_viewport(
|
||||
unsigned int x,
|
||||
unsigned int y,
|
||||
unsigned int width,
|
||||
unsigned int height
|
||||
) override;
|
||||
|
||||
private:
|
||||
Ref<dxSharedContext> m_context;
|
||||
|
||||
void set_resolution(unsigned int width, unsigned int height);
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
#include <d3dcompiler.h>
|
||||
#include <renderer/dx/shader.hpp>
|
||||
#include <renderer/dx/shared_context.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
dxShader::dxShader(
|
||||
BasicFileHandle vertexFile,
|
||||
BasicFileHandle pixelFile,
|
||||
Ref<dxSharedContext> sharedContext
|
||||
)
|
||||
: m_context(sharedContext)
|
||||
, m_vertex_shader(nullptr)
|
||||
, m_pixel_shader(nullptr)
|
||||
, m_vertex_blob(nullptr)
|
||||
{
|
||||
auto ps = Microsoft::WRL::ComPtr<ID3DBlob> { nullptr };
|
||||
auto vsErr = Microsoft::WRL::ComPtr<ID3DBlob> { nullptr };
|
||||
auto psErr = Microsoft::WRL::ComPtr<ID3DBlob> { nullptr };
|
||||
|
||||
// compile shaders (we don't use dxc here because if d3_d_compile fails it throws a dxException
|
||||
// without logging the vsErr/psErr
|
||||
d3_d_compile(
|
||||
vertexFile.GetData(),
|
||||
vertexFile.get_size(),
|
||||
NULL,
|
||||
nullptr,
|
||||
nullptr,
|
||||
"main",
|
||||
"vs_4_0",
|
||||
NULL,
|
||||
NULL,
|
||||
&m_vertex_blob,
|
||||
&vsErr
|
||||
);
|
||||
d3_d_compile(
|
||||
pixelFile.GetData(),
|
||||
pixelFile.get_size(),
|
||||
NULL,
|
||||
nullptr,
|
||||
nullptr,
|
||||
"main",
|
||||
"ps_4_0",
|
||||
NULL,
|
||||
NULL,
|
||||
&ps,
|
||||
&psErr
|
||||
);
|
||||
|
||||
// check
|
||||
ensure(!vsErr.Get(), "Vertex shader compile error: {}", (char *)vsErr->GetBufferPointer());
|
||||
ensure(!psErr.Get(), "Pixels shader compile error: {}", (char *)psErr->GetBufferPointer());
|
||||
|
||||
// create shaders
|
||||
auto hr = HRESULT {};
|
||||
dxc(m_context->get_device()->CreateVertexShader(
|
||||
m_vertex_blob->GetBufferPointer(),
|
||||
m_vertex_blob->GetBufferSize(),
|
||||
NULL,
|
||||
&m_vertex_shader
|
||||
));
|
||||
dxc(
|
||||
m_context->get_device()
|
||||
->CreatePixelShader(ps->GetBufferPointer(), ps->GetBufferSize(), NULL, &m_pixel_shader)
|
||||
);
|
||||
}
|
||||
|
||||
dxShader::~dxShader()
|
||||
{
|
||||
un_bind();
|
||||
}
|
||||
|
||||
void dxShader::bind()
|
||||
{
|
||||
m_context->get_device_context()->VSSetShader(m_vertex_shader.Get(), nullptr, 0u);
|
||||
m_context->get_device_context()->PSSetShader(m_pixel_shader.Get(), nullptr, 0u);
|
||||
}
|
||||
|
||||
void dxShader::un_bind()
|
||||
{
|
||||
m_context->get_device_context()->VSSetShader(nullptr, nullptr, 0u);
|
||||
m_context->get_device_context()->PSSetShader(nullptr, nullptr, 0u);
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <renderer/shader.hpp>
|
||||
#include <wrl.h>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class dxSharedContext;
|
||||
|
||||
class dxShader: public Shader
|
||||
{
|
||||
public:
|
||||
dxShader(
|
||||
BasicFileHandle vertexFile,
|
||||
BasicFileHandle pixelFile,
|
||||
Ref<dxSharedContext> sharedContext
|
||||
);
|
||||
|
||||
~dxShader();
|
||||
|
||||
void bind() override;
|
||||
|
||||
void un_bind() override;
|
||||
|
||||
auto get_vertex_blob() -> Microsoft::WRL::ComPtr<ID3DBlob>
|
||||
{
|
||||
return m_vertex_blob;
|
||||
}
|
||||
|
||||
private:
|
||||
Ref<dxSharedContext> m_context;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D11VertexShader> m_vertex_shader;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D11PixelShader> m_pixel_shader;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3DBlob> m_vertex_blob;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <renderer/shared_context.hpp>
|
||||
#include <wrl.h>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class dxSharedContext: public SharedContext
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] auto get_device() -> Microsoft::WRL::ComPtr<ID3D11Device>
|
||||
{
|
||||
return m_device;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_device_context() -> Microsoft::WRL::ComPtr<ID3D11DeviceContext>
|
||||
{
|
||||
return m_deviceContext;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_swap_chain() -> Microsoft::WRL::ComPtr<IDXGISwapChain>
|
||||
{
|
||||
return m_swap_chain;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_render_target_view() -> Microsoft::WRL::ComPtr<ID3D11RenderTargetView>
|
||||
{
|
||||
return m_render_target_view;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto GetDeviceRef() -> Microsoft::WRL::ComPtr<ID3D11Device>
|
||||
{
|
||||
return m_device;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto GetDeviceContextRef() -> Microsoft::WRL::ComPtr<ID3D11DeviceContext>
|
||||
{
|
||||
return m_deviceContext;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto GetSwapChainRef() -> Microsoft::WRL::ComPtr<IDXGISwapChain>
|
||||
{
|
||||
return m_swap_chain;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto GetRenderTargetViewRef() -> Microsoft::WRL::ComPtr<ID3D11RenderTargetView>
|
||||
{
|
||||
return m_render_target_view;
|
||||
}
|
||||
|
||||
private:
|
||||
Microsoft::WRL::ComPtr<ID3D11Device> m_device = nullptr;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D11DeviceContext> m_deviceContext = nullptr;
|
||||
|
||||
Microsoft::WRL::ComPtr<IDXGISwapChain> m_swap_chain = nullptr;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D11RenderTargetView> m_render_target_view = nullptr;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
#include <renderer/dx/shared_context.hpp>
|
||||
#include <renderer/dx/texture.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
dxTexture::dxTexture(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
unsigned int components,
|
||||
unsigned char *pixels,
|
||||
Ref<dxSharedContext> sharedContext,
|
||||
const std::string &filePath
|
||||
)
|
||||
: Texture(filePath)
|
||||
, m_context(sharedContext)
|
||||
, m_texture_2d(nullptr)
|
||||
, m_shader_resource_view(nullptr)
|
||||
, m_sampler_state(nullptr)
|
||||
{
|
||||
// texture2d desc
|
||||
auto t2dDesc = D3D11_TEXTURE2D_DESC {};
|
||||
t2dDesc.Width = width;
|
||||
t2dDesc.Height = height;
|
||||
t2dDesc.MipLevels = 0u;
|
||||
t2dDesc.ArraySize = 1u;
|
||||
t2dDesc.Format = components == 4u ? DXGI_FORMAT_R8G8B8A8_UNORM :
|
||||
components == 3u ? DXGI_FORMAT_R8G8B8A8_UNORM :
|
||||
// #todo: figure out what to do with this bitch ._.
|
||||
components == 2u ? DXGI_FORMAT_R8G8_UNORM :
|
||||
components == 1u ? DXGI_FORMAT_R8_UNORM :
|
||||
DXGI_FORMAT_UNKNOWN;
|
||||
t2dDesc.SampleDesc.Count = 1u;
|
||||
t2dDesc.SampleDesc.Quality = 0u;
|
||||
t2dDesc.Usage = D3D11_USAGE_DEFAULT;
|
||||
t2dDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
|
||||
t2dDesc.CPUAccessFlags = NULL;
|
||||
t2dDesc.MiscFlags = D3D11_RESOURCE_MISC_GENERATE_MIPS;
|
||||
|
||||
// create texture
|
||||
auto hr = HRESULT {};
|
||||
dxc(m_context->get_device()->CreateTexture2D(&t2dDesc, nullptr, &m_texture_2d));
|
||||
m_context->get_device_context()
|
||||
->UpdateSubresource(m_texture_2d.Get(), 0u, nullptr, pixels, width * 4u, 0u);
|
||||
|
||||
m_texture_2d->GetDesc(&t2dDesc);
|
||||
|
||||
// shader resource view desc
|
||||
auto srvDesc = D3D11_SHADER_RESOURCE_VIEW_DESC {};
|
||||
srvDesc.Format = t2dDesc.Format;
|
||||
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
||||
srvDesc.Texture2D.MostDetailedMip = 0u;
|
||||
srvDesc.Texture2D.MipLevels = -1;
|
||||
|
||||
// create shader resource view
|
||||
m_context->get_device()
|
||||
->CreateShaderResourceView(m_texture_2d.Get(), &srvDesc, &m_shader_resource_view);
|
||||
m_context->get_device_context()->GenerateMips(m_shader_resource_view.Get());
|
||||
|
||||
// sampler desc
|
||||
auto sDesc = D3D11_SAMPLER_DESC {};
|
||||
sDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
sDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
sDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
sDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
sDesc.MinLOD = 0.0f;
|
||||
sDesc.MipLODBias = 0.0f;
|
||||
sDesc.MaxLOD = D3D11_FLOAT32_MAX;
|
||||
|
||||
// create sampler
|
||||
m_context->get_device()->CreateSamplerState(&sDesc, &m_sampler_state);
|
||||
}
|
||||
|
||||
void dxTexture::bind(unsigned int slot /* = 0u */)
|
||||
{
|
||||
m_context->get_device_context()->PSSetSamplers(slot, 1u, m_sampler_state.GetAddressOf());
|
||||
m_context->get_device_context()
|
||||
->PSSetShaderResources(slot, 1u, m_shader_resource_view.GetAddressOf());
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <renderer/texture.hpp>
|
||||
#include <wrl.h>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class dxSharedContext;
|
||||
|
||||
class dxTexture: public Texture
|
||||
{
|
||||
public:
|
||||
dxTexture(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
unsigned int components,
|
||||
unsigned char *pixels,
|
||||
Ref<dxSharedContext> sharedContext,
|
||||
const std::string &filePath
|
||||
);
|
||||
|
||||
void bind(unsigned int slot = 0u) override;
|
||||
|
||||
private:
|
||||
Ref<dxSharedContext> m_context;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D11Texture2D> m_texture_2d;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> m_shader_resource_view;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D11SamplerState> m_sampler_state;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <ui/ui.hpp>
|
||||
#include <wrl.h>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class dxSharedContext;
|
||||
|
||||
class dxUserInterface: public UserInterface
|
||||
{
|
||||
public:
|
||||
dxUserInterface() = default;
|
||||
|
||||
~dxUserInterface();
|
||||
|
||||
void platform_implementation(Ref<SharedContext> sharedContext) override;
|
||||
|
||||
void begin() override;
|
||||
|
||||
void end() override;
|
||||
|
||||
void log_debug_data() override;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
#include <renderer/dx/shader.hpp>
|
||||
#include <renderer/dx/shared_context.hpp>
|
||||
#include <renderer/dx/vertex_layout.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
dxVertexLayout::dxVertexLayout(
|
||||
Ref<Shader> shader,
|
||||
const std::vector<std::pair<std::string, VertexElementType>> &elements,
|
||||
Ref<dxSharedContext> sharedContext
|
||||
)
|
||||
: m_context(sharedContext)
|
||||
, m_input_layout(nullptr)
|
||||
{
|
||||
auto inputElementsDesc = std::vector<D3D11_INPUT_ELEMENT_DESC> {};
|
||||
inputElementsDesc.reserve(elements.size());
|
||||
|
||||
// extract elements desc
|
||||
for (const auto &element : elements)
|
||||
{
|
||||
inputElementsDesc.emplace_back(
|
||||
D3D11_INPUT_ELEMENT_DESC { element.first.c_str(),
|
||||
NULL,
|
||||
get_dxgi_format(element.second),
|
||||
0u,
|
||||
D3D11_APPEND_ALIGNED_ELEMENT,
|
||||
D3D11_INPUT_PER_VERTEX_DATA,
|
||||
0u }
|
||||
);
|
||||
}
|
||||
|
||||
auto dxpShader = std::dynamic_pointer_cast<dxShader>(shader);
|
||||
ensure(dxpShader, "Failed to cast 'Shader' to 'dxShader'");
|
||||
|
||||
// create input layout (vertex layout)
|
||||
auto hr = HRESULT {};
|
||||
dxc(m_context->get_device()->CreateInputLayout(
|
||||
&inputElementsDesc[0],
|
||||
inputElementsDesc.size(),
|
||||
dxpShader->get_vertex_blob().Get()->GetBufferPointer(),
|
||||
dxpShader->get_vertex_blob().Get()->GetBufferSize(),
|
||||
&m_input_layout
|
||||
));
|
||||
}
|
||||
|
||||
dxVertexLayout::~dxVertexLayout()
|
||||
{
|
||||
un_bind();
|
||||
}
|
||||
|
||||
void dxVertexLayout::bind()
|
||||
{
|
||||
m_context->get_device_context()->IASetInputLayout(m_input_layout.Get());
|
||||
}
|
||||
|
||||
void dxVertexLayout::un_bind()
|
||||
{
|
||||
m_context->get_device_context()->IASetInputLayout(nullptr);
|
||||
}
|
||||
|
||||
auto dxVertexLayout::get_dxgi_format(VertexElementType type) -> DXGI_FORMAT
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
/* byte */
|
||||
case lt::VertexElementType::Byte1: return DXGI_FORMAT_R8_SINT;
|
||||
case lt::VertexElementType::Byte2: return DXGI_FORMAT_R8G8_SINT;
|
||||
case lt::VertexElementType::Byte4: return DXGI_FORMAT_R8_SINT;
|
||||
|
||||
/* ubyte */
|
||||
case lt::VertexElementType::UByte1: return DXGI_FORMAT_R8_UINT;
|
||||
case lt::VertexElementType::UByte2: return DXGI_FORMAT_R8G8_UINT;
|
||||
case lt::VertexElementType::UByte4: return DXGI_FORMAT_R8G8B8A8_UINT;
|
||||
|
||||
/* int */
|
||||
case lt::VertexElementType::Int1: return DXGI_FORMAT_R32_SINT;
|
||||
case lt::VertexElementType::Int2: return DXGI_FORMAT_R32G32_SINT;
|
||||
case lt::VertexElementType::Int3: return DXGI_FORMAT_R32G32B32_SINT;
|
||||
case lt::VertexElementType::Int4: return DXGI_FORMAT_R32G32B32A32_SINT;
|
||||
|
||||
/* uint */
|
||||
case lt::VertexElementType::UInt1: return DXGI_FORMAT_R32_UINT;
|
||||
case lt::VertexElementType::UInt2: return DXGI_FORMAT_R32G32_UINT;
|
||||
case lt::VertexElementType::UInt3: return DXGI_FORMAT_R32G32B32_UINT;
|
||||
case lt::VertexElementType::UInt4: return DXGI_FORMAT_R32G32B32A32_UINT;
|
||||
|
||||
/* float */
|
||||
case lt::VertexElementType::Float1: return DXGI_FORMAT_R32_FLOAT;
|
||||
case lt::VertexElementType::Float2: return DXGI_FORMAT_R32G32_FLOAT;
|
||||
case lt::VertexElementType::Float3: return DXGI_FORMAT_R32G32B32_FLOAT;
|
||||
case lt::VertexElementType::Float4: return DXGI_FORMAT_R32G32B32A32_FLOAT;
|
||||
|
||||
default: ensure(false, "Invalid 'VertexElementType'"); return DXGI_FORMAT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <renderer/vertex_layout.hpp>
|
||||
#include <wrl.h>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class Shader;
|
||||
class dxSharedContext;
|
||||
|
||||
class dxVertexLayout: public VertexLayout
|
||||
{
|
||||
public:
|
||||
dxVertexLayout(
|
||||
Ref<Shader> shader,
|
||||
const std::vector<std::pair<std::string, VertexElementType>> &elements,
|
||||
Ref<dxSharedContext> sharedContext
|
||||
);
|
||||
|
||||
~dxVertexLayout();
|
||||
|
||||
void bind() override;
|
||||
|
||||
void un_bind() override;
|
||||
|
||||
private:
|
||||
DXGI_FORMAT get_dxgi_format(VertexElementType type);
|
||||
|
||||
Ref<dxSharedContext> m_context;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D11InputLayout> m_input_layout;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
#include <renderer/framebuffer.hpp>
|
||||
#include <renderer/gl/framebuffers.hpp>
|
||||
|
||||
#ifdef LIGHT_PLATFORM_WINDOWS
|
||||
#include <renderer/dx/framebuffer.hpp>
|
||||
#include <renderer/dx/sharedcontext.hpp>
|
||||
#endif
|
||||
|
||||
#include <renderer/graphics_context.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
auto Framebuffer::create(
|
||||
const FramebufferSpecification &specification,
|
||||
const Ref<SharedContext> & /*sharedContext*/
|
||||
) -> Ref<Framebuffer>
|
||||
{
|
||||
switch (GraphicsContext::get_graphics_api())
|
||||
{
|
||||
case GraphicsAPI::OpenGL: return create_ref<glFramebuffer>(specification);
|
||||
|
||||
case GraphicsAPI::DirectX:
|
||||
lt_win(
|
||||
return create_ref<dxFramebuffer>(
|
||||
specification,
|
||||
std::static_pointer_cast<dxSharedContext>(sharedContext)
|
||||
);
|
||||
);
|
||||
|
||||
default:
|
||||
ensure(
|
||||
false,
|
||||
"Invalid/unsupported 'GraphicsAPI' {}",
|
||||
static_cast<uint32_t>(GraphicsContext::get_graphics_api())
|
||||
);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
#include <renderer/gl/blender.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
glBlender::glBlender()
|
||||
: m_factor_map { // constants
|
||||
{ BlendFactor::ZERO, GL_ZERO },
|
||||
{ BlendFactor::ONE, GL_ONE },
|
||||
|
||||
// source ,
|
||||
{ BlendFactor::SRC_COLOR, GL_SRC_COLOR },
|
||||
{ BlendFactor::INVERSE_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR },
|
||||
|
||||
{ BlendFactor::SRC_ALPHA, GL_SRC_ALPHA },
|
||||
{ BlendFactor::INVERSE_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
|
||||
|
||||
// destination ,
|
||||
{ BlendFactor::DST_COLOR, GL_DST_COLOR },
|
||||
{ BlendFactor::INVERSE_DST_COLOR, GL_ONE_MINUS_DST_COLOR },
|
||||
|
||||
{ BlendFactor::DST_ALPHA, GL_DST_ALPHA },
|
||||
{ BlendFactor::INVERSE_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA },
|
||||
|
||||
// source1 ,
|
||||
{ BlendFactor::SRC1_COLOR, GL_SRC1_COLOR },
|
||||
{ BlendFactor::INVERSE_SRC1_COLOR, GL_ONE_MINUS_SRC1_COLOR },
|
||||
|
||||
{ BlendFactor::SRC1_ALPHA, GL_SRC1_ALPHA },
|
||||
{ BlendFactor::INVERSE_SRC1_ALPHA, GL_ONE_MINUS_SRC_ALPHA }
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
void glBlender::enable(BlendFactor srcFactor, BlendFactor dstFactor)
|
||||
{
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(m_factor_map.at(srcFactor), m_factor_map.at(dstFactor));
|
||||
}
|
||||
|
||||
void glBlender::disable()
|
||||
{
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include <renderer/blender.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class glBlender: public Blender
|
||||
{
|
||||
public:
|
||||
~glBlender() override = default;
|
||||
glBlender();
|
||||
|
||||
void enable(BlendFactor srcFactor, BlendFactor dstFactor) override;
|
||||
|
||||
void disable() override;
|
||||
|
||||
private:
|
||||
std::unordered_map<BlendFactor, unsigned int> m_factor_map;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
#include <cstddef>
|
||||
#include <logger/logger.hpp>
|
||||
#include <renderer/gl/buffers.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
//==================== CONSTANT_BUFFER ====================//
|
||||
glConstantBuffer::glConstantBuffer(ConstantBufferIndex index, unsigned int size)
|
||||
: m_buffer_id()
|
||||
, m_index(static_cast<int>(index))
|
||||
{
|
||||
glCreateBuffers(1, &m_buffer_id);
|
||||
glNamedBufferData(m_buffer_id, size, nullptr, GL_DYNAMIC_DRAW);
|
||||
|
||||
bind();
|
||||
}
|
||||
|
||||
glConstantBuffer::~glConstantBuffer()
|
||||
{
|
||||
glDeleteBuffers(1, &m_buffer_id);
|
||||
}
|
||||
|
||||
void glConstantBuffer::bind()
|
||||
{
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, m_index, m_buffer_id);
|
||||
}
|
||||
|
||||
auto glConstantBuffer::map() -> void *
|
||||
{
|
||||
auto *map = glMapNamedBuffer(m_buffer_id, GL_WRITE_ONLY);
|
||||
return map;
|
||||
}
|
||||
|
||||
void glConstantBuffer::un_map()
|
||||
{
|
||||
glUnmapNamedBuffer(m_buffer_id);
|
||||
}
|
||||
//==================== CONSTANT_BUFFER ====================//
|
||||
|
||||
//==================== VERTEX_BUFFER ====================//
|
||||
glVertexBuffer::glVertexBuffer(float *vertices, unsigned int stride, unsigned int count)
|
||||
: m_buffer_id()
|
||||
{
|
||||
glCreateBuffers(1, &m_buffer_id);
|
||||
glNamedBufferData(
|
||||
m_buffer_id,
|
||||
static_cast<GLsizeiptr>(stride * count),
|
||||
vertices,
|
||||
GL_DYNAMIC_DRAW
|
||||
);
|
||||
}
|
||||
|
||||
glVertexBuffer::~glVertexBuffer()
|
||||
{
|
||||
glDeleteBuffers(1, &m_buffer_id);
|
||||
}
|
||||
|
||||
void glVertexBuffer::bind()
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_buffer_id);
|
||||
}
|
||||
|
||||
void glVertexBuffer::un_bind()
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, {});
|
||||
}
|
||||
|
||||
auto glVertexBuffer::map() -> void *
|
||||
{
|
||||
return glMapNamedBuffer(m_buffer_id, GL_WRITE_ONLY);
|
||||
}
|
||||
|
||||
void glVertexBuffer::un_map()
|
||||
{
|
||||
glUnmapNamedBuffer(m_buffer_id);
|
||||
}
|
||||
//==================== VERTEX_BUFFER ====================//
|
||||
|
||||
//==================== INDEX_BUFFER ====================//
|
||||
glIndexBuffer::glIndexBuffer(unsigned int *indices, unsigned int count): m_buffer_id()
|
||||
{
|
||||
// generate indices if not provided
|
||||
auto hasIndices = !!indices;
|
||||
if (!hasIndices)
|
||||
{
|
||||
// check
|
||||
if (count % 6 != 0)
|
||||
{
|
||||
log_wrn("'indices' can only be null if count is multiple of 6");
|
||||
log_wrn("Adding {} to 'count' -> {}", (6 - (count % 6)), count + (6 - (count % 6)));
|
||||
count = count + (6 - (count % 6));
|
||||
}
|
||||
|
||||
// create indices
|
||||
indices = new unsigned int[count];
|
||||
auto offset = 0u;
|
||||
for (auto i = 0u; i < count; i += 6u)
|
||||
{
|
||||
indices[i + 0] = offset + 0u;
|
||||
indices[i + 1] = offset + 1u;
|
||||
indices[i + 2] = offset + 2u;
|
||||
|
||||
indices[i + 3] = offset + 2u;
|
||||
indices[i + 4] = offset + 3u;
|
||||
indices[i + 5] = offset + 0u;
|
||||
|
||||
offset += 4u;
|
||||
}
|
||||
}
|
||||
|
||||
// create buffer
|
||||
glCreateBuffers(1, &m_buffer_id);
|
||||
glNamedBufferData(m_buffer_id, count * sizeof(unsigned int), indices, GL_STATIC_DRAW);
|
||||
|
||||
// delete indices
|
||||
if (!hasIndices)
|
||||
{
|
||||
delete[] indices;
|
||||
}
|
||||
}
|
||||
|
||||
glIndexBuffer::~glIndexBuffer()
|
||||
{
|
||||
glDeleteBuffers(1, &m_buffer_id);
|
||||
}
|
||||
|
||||
void glIndexBuffer::bind()
|
||||
{
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_buffer_id);
|
||||
}
|
||||
|
||||
void glIndexBuffer::un_bind()
|
||||
{
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, {});
|
||||
}
|
||||
//==================== INDEX_BUFFER ====================//
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include <renderer/buffers.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class glConstantBuffer: public ConstantBuffer
|
||||
{
|
||||
public:
|
||||
glConstantBuffer(ConstantBufferIndex index, unsigned int size);
|
||||
|
||||
~glConstantBuffer() override;
|
||||
|
||||
void bind() override;
|
||||
|
||||
auto map() -> void * override;
|
||||
|
||||
void un_map() override;
|
||||
|
||||
private:
|
||||
unsigned int m_buffer_id;
|
||||
|
||||
unsigned int m_index;
|
||||
};
|
||||
|
||||
class glVertexBuffer: public VertexBuffer
|
||||
{
|
||||
public:
|
||||
glVertexBuffer(float *vertices, unsigned int stride, unsigned int count);
|
||||
|
||||
~glVertexBuffer() override;
|
||||
|
||||
void bind() override;
|
||||
|
||||
void un_bind() override;
|
||||
|
||||
auto map() -> void * override;
|
||||
|
||||
void un_map() override;
|
||||
|
||||
private:
|
||||
unsigned int m_buffer_id;
|
||||
};
|
||||
|
||||
class glIndexBuffer: public IndexBuffer
|
||||
{
|
||||
public:
|
||||
glIndexBuffer(unsigned int *indices, unsigned int count);
|
||||
|
||||
~glIndexBuffer() override;
|
||||
|
||||
void bind() override;
|
||||
|
||||
void un_bind() override;
|
||||
|
||||
private:
|
||||
unsigned int m_buffer_id;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
#include <renderer/gl/framebuffers.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
glFramebuffer::glFramebuffer(const FramebufferSpecification &specification)
|
||||
: m_specification(specification)
|
||||
, m_buffer_id()
|
||||
, m_color_attachment_id()
|
||||
, m_depth_stencil_attachment_id()
|
||||
{
|
||||
resize({ specification.width, specification.height });
|
||||
}
|
||||
|
||||
glFramebuffer::~glFramebuffer()
|
||||
{
|
||||
glDeleteFramebuffers(1, &m_buffer_id);
|
||||
glDeleteTextures(1, &m_color_attachment_id);
|
||||
// glDeleteTextures(1, &m_depth_stencil_attachment_id);
|
||||
}
|
||||
|
||||
void glFramebuffer::bind_as_target(const math::vec4 &clearColor)
|
||||
{
|
||||
// #todo: use viewport instead of default x=0, y=0
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, m_buffer_id);
|
||||
glViewport(0, 0, m_specification.width, m_specification.height);
|
||||
|
||||
glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void glFramebuffer::bind_as_resource()
|
||||
{
|
||||
log_err("NO_IMPLEMENT!");
|
||||
}
|
||||
|
||||
void glFramebuffer::resize(const math::uvec2 &size)
|
||||
{
|
||||
if (m_buffer_id)
|
||||
{
|
||||
glDeleteFramebuffers(1, &m_buffer_id);
|
||||
glDeleteTextures(1, &m_color_attachment_id);
|
||||
// glDeleteTextures(1, &m_depth_stencil_attachment_id);
|
||||
}
|
||||
|
||||
m_specification.width = std::clamp(size.x, 1u, (unsigned int)GL_MAX_TEXTURE_SIZE);
|
||||
m_specification.height = std::clamp(size.y, 1u, (unsigned int)GL_MAX_TEXTURE_SIZE);
|
||||
|
||||
glCreateFramebuffers(1, &m_buffer_id);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, m_buffer_id);
|
||||
|
||||
// create color attachment
|
||||
glCreateTextures(GL_TEXTURE_2D, 1, &m_color_attachment_id);
|
||||
glBindTexture(GL_TEXTURE_2D, m_color_attachment_id);
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RGBA8,
|
||||
m_specification.width,
|
||||
m_specification.height,
|
||||
{},
|
||||
GL_RGBA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
nullptr
|
||||
);
|
||||
glTextureParameteri(m_color_attachment_id, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTextureParameteri(m_color_attachment_id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glFramebufferTexture2D(
|
||||
GL_FRAMEBUFFER,
|
||||
GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D,
|
||||
m_color_attachment_id,
|
||||
0
|
||||
);
|
||||
|
||||
ensure(
|
||||
(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE),
|
||||
"Framebuffer is incomplete"
|
||||
);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <math/vec2.hpp>
|
||||
#include <math/vec4.hpp>
|
||||
#include <renderer/framebuffer.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class glFramebuffer: public Framebuffer
|
||||
{
|
||||
public:
|
||||
glFramebuffer(const FramebufferSpecification &specification);
|
||||
|
||||
~glFramebuffer() override;
|
||||
|
||||
void bind_as_target(const math::vec4 &clearColor) override;
|
||||
|
||||
void bind_as_resource() override;
|
||||
|
||||
void resize(const math::uvec2 &size) override;
|
||||
|
||||
auto get_color_attachment() -> void * override
|
||||
{
|
||||
return (void *)m_color_attachment_id;
|
||||
}
|
||||
|
||||
private:
|
||||
FramebufferSpecification m_specification;
|
||||
|
||||
unsigned int m_buffer_id;
|
||||
|
||||
unsigned int m_color_attachment_id;
|
||||
|
||||
unsigned int m_depth_stencil_attachment_id;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
#include <input/events/window.hpp>
|
||||
#include <renderer/blender.hpp> // required for forward declaratio>
|
||||
#include <renderer/buffers.hpp> // required for forward declaratio>
|
||||
#include <renderer/gl/graphics_context.hpp>
|
||||
#include <renderer/render_command.hpp> // required for forward declaratio>
|
||||
#include <renderer/renderer.hpp> // required for forward declaratio>
|
||||
|
||||
namespace lt {
|
||||
|
||||
glGraphicsContext::glGraphicsContext()
|
||||
{
|
||||
m_graphics_api = GraphicsAPI::OpenGL;
|
||||
set_debug_message_callback();
|
||||
}
|
||||
|
||||
void glGraphicsContext::log_debug_data()
|
||||
{
|
||||
log_inf("________________________________________");
|
||||
log_inf("GraphicsContext::");
|
||||
log_inf(" API : OpenGL");
|
||||
log_inf("________________________________________");
|
||||
}
|
||||
|
||||
void glGraphicsContext::set_debug_message_callback()
|
||||
{
|
||||
// determine log level
|
||||
// #todo: set filters from config.h
|
||||
#if defined(LIGHT_DEBUG)
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
|
||||
|
||||
GLuint ids[] = { 131185 };
|
||||
glDebugMessageControl(
|
||||
GL_DEBUG_SOURCE_API,
|
||||
GL_DEBUG_TYPE_OTHER,
|
||||
GL_DONT_CARE,
|
||||
_countof(ids),
|
||||
ids,
|
||||
GL_FALSE
|
||||
);
|
||||
#elif defined(LIGHT_RELEASE)
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_FALSE);
|
||||
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, nullptr, GL_TRUE);
|
||||
glDebugMessageControl(
|
||||
GL_DONT_CARE,
|
||||
GL_DONT_CARE,
|
||||
GL_DEBUG_SEVERITY_MEDIUM,
|
||||
0,
|
||||
nullptr,
|
||||
GL_TRUE
|
||||
);
|
||||
#else // LIGHT_DIST
|
||||
return;
|
||||
#endif
|
||||
|
||||
/* setup message callback */
|
||||
glDebugMessageCallback(
|
||||
[](unsigned int source,
|
||||
unsigned int type,
|
||||
unsigned int id,
|
||||
unsigned int severity,
|
||||
int /*length*/,
|
||||
const char *message,
|
||||
const void * /*userParam*/) {
|
||||
switch (severity)
|
||||
{
|
||||
case GL_DEBUG_SEVERITY_HIGH:
|
||||
// TODO(Light): Add gl exception class
|
||||
throw std::runtime_error { "gl exception" };
|
||||
return;
|
||||
|
||||
case GL_DEBUG_SEVERITY_MEDIUM:
|
||||
case GL_DEBUG_SEVERITY_LOW:
|
||||
log_wrn(
|
||||
"glMessageCallback: Severity: {} :: Source: {} :: Type: {} :: ID: {}",
|
||||
// TODO(Light): Stringify!
|
||||
severity,
|
||||
source,
|
||||
type,
|
||||
id
|
||||
);
|
||||
log_wrn(" {}", message);
|
||||
return;
|
||||
|
||||
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
||||
log_wrn(
|
||||
"Severity: {} :: Source: {} :: Type: {} :: ID: {}",
|
||||
// TODO(Light): Stringify!
|
||||
severity,
|
||||
source,
|
||||
type,
|
||||
id
|
||||
);
|
||||
log_trc(" {}", message);
|
||||
return;
|
||||
}
|
||||
},
|
||||
nullptr
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include <renderer/graphics_context.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class glGraphicsContext: public GraphicsContext
|
||||
{
|
||||
public:
|
||||
glGraphicsContext();
|
||||
|
||||
void log_debug_data() override;
|
||||
|
||||
private:
|
||||
void set_debug_message_callback();
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
#include <renderer/gl/render_command.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
glRenderCommand::glRenderCommand(): m_window_handle(windowHandle)
|
||||
{
|
||||
}
|
||||
|
||||
void glRenderCommand::swap_buffers()
|
||||
{
|
||||
}
|
||||
|
||||
void glRenderCommand::clear_back_buffer(const math::vec4 &clearColor)
|
||||
{
|
||||
glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void glRenderCommand::draw(unsigned int count)
|
||||
{
|
||||
glDrawArrays(GL_TRIANGLES, 0, count);
|
||||
}
|
||||
|
||||
void glRenderCommand::draw_indexed(unsigned int count)
|
||||
{
|
||||
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, nullptr);
|
||||
}
|
||||
|
||||
void glRenderCommand::default_target_framebuffer()
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, {});
|
||||
}
|
||||
|
||||
void glRenderCommand::set_viewport(
|
||||
unsigned int x,
|
||||
unsigned int y,
|
||||
unsigned int width,
|
||||
unsigned int height
|
||||
)
|
||||
{
|
||||
glViewport(x, y, width, height);
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <math/vec4.hpp>
|
||||
#include <renderer/render_command.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class glRenderCommand: public RenderCommand
|
||||
{
|
||||
public:
|
||||
glRenderCommand();
|
||||
|
||||
void swap_buffers() override;
|
||||
|
||||
void clear_back_buffer(const math::vec4 &clearColor) override;
|
||||
|
||||
void draw(unsigned int count) override;
|
||||
|
||||
void draw_indexed(unsigned int count) override;
|
||||
|
||||
void default_target_framebuffer() override;
|
||||
|
||||
void set_viewport(
|
||||
unsigned int x,
|
||||
unsigned int y,
|
||||
unsigned int width,
|
||||
unsigned int height
|
||||
) override;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
#include <asset_parser/assets/text.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
glShader::glShader(
|
||||
const Ref<Assets::TextAsset> &vertex_asset,
|
||||
const Ref<Assets::TextAsset> &pixel_asset
|
||||
)
|
||||
: m_shader_id(glCreateProgram())
|
||||
{
|
||||
auto vertex_blob_metadata = vertex_asset->get_blob_metadata(Assets::BlobMetadata::Tag::text);
|
||||
auto pixel_blob_metadata = pixel_asset->get_blob_metadata(Assets::BlobMetadata::Tag::text);
|
||||
|
||||
auto vertex_blob = Assets::Blob(vertex_blob_metadata.uncompressed_size);
|
||||
auto pixel_blob = Assets::Blob(pixel_blob_metadata.uncompressed_size);
|
||||
|
||||
vertex_asset->unpack_blob(vertex_blob_metadata.tag, vertex_blob.data(), vertex_blob.size());
|
||||
pixel_asset->unpack_blob(pixel_blob_metadata.tag, pixel_blob.data(), pixel_blob.size());
|
||||
|
||||
auto vertex_source = std::string {
|
||||
reinterpret_cast<char *>(vertex_blob.data()),
|
||||
reinterpret_cast<char *>(vertex_blob.data()) + vertex_blob.size(), // NOLINT
|
||||
};
|
||||
|
||||
auto pixel_source = std::string {
|
||||
reinterpret_cast<char *>(pixel_blob.data()),
|
||||
reinterpret_cast<char *>(pixel_blob.data()) + pixel_blob.size(), // NOLINT
|
||||
};
|
||||
|
||||
const auto vertex_shader = compile_shader(vertex_source, Shader::Stage::vertex);
|
||||
const auto pixel_shader = compile_shader(pixel_source, Shader::Stage::pixel);
|
||||
|
||||
glAttachShader(m_shader_id, vertex_shader);
|
||||
glAttachShader(m_shader_id, pixel_shader);
|
||||
|
||||
glLinkProgram(m_shader_id);
|
||||
|
||||
glDeleteShader(vertex_shader);
|
||||
glDeleteShader(pixel_shader);
|
||||
}
|
||||
|
||||
glShader::~glShader()
|
||||
{
|
||||
glDeleteProgram(m_shader_id);
|
||||
}
|
||||
|
||||
void glShader::bind()
|
||||
{
|
||||
glUseProgram(m_shader_id);
|
||||
}
|
||||
|
||||
void glShader::un_bind()
|
||||
{
|
||||
glUseProgram({});
|
||||
}
|
||||
|
||||
auto glShader::compile_shader(const std::string &source, Shader::Stage stage) -> unsigned int
|
||||
{
|
||||
// &(address of) needs an lvalue
|
||||
const auto *lvalue_source = source.c_str();
|
||||
auto shader = glCreateShader(
|
||||
stage == Shader::Stage::vertex ? GL_VERTEX_SHADER :
|
||||
stage == Shader::Stage::pixel ? GL_FRAGMENT_SHADER :
|
||||
stage == Shader::Stage::geometry ? GL_GEOMETRY_SHADER :
|
||||
0
|
||||
);
|
||||
|
||||
// compile
|
||||
glShaderSource(shader, 1, &lvalue_source, nullptr);
|
||||
glCompileShader(shader);
|
||||
|
||||
// check
|
||||
auto isCompiled = 0;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
|
||||
if (isCompiled == GL_FALSE)
|
||||
{
|
||||
auto logLength = 0;
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
|
||||
|
||||
auto *errorLog = (char *)alloca(logLength);
|
||||
glGetShaderInfoLog(shader, logLength, &logLength, &errorLog[0]);
|
||||
|
||||
log_err(
|
||||
"glShader::glShader: failed to compile {} shader:\n {}",
|
||||
stage == Shader::Stage::vertex ? "Vertex" : "Pixel",
|
||||
errorLog
|
||||
);
|
||||
|
||||
return {};
|
||||
}
|
||||
#define LIGHT_OPENGL_ENABLE_SHADER_INFO_LOG
|
||||
#ifdef LIGHT_OPENGL_ENABLE_SHADER_INFO_LOG
|
||||
// info log
|
||||
{
|
||||
auto logLength = 0;
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
|
||||
if (logLength)
|
||||
{
|
||||
char *infoLog = (char *)alloca(logLength);
|
||||
glGetShaderInfoLog(shader, logLength, &logLength, &infoLog[0]);
|
||||
|
||||
log_wrn("Shader info: {}", infoLog);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <renderer/shader.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class glShader: public Shader
|
||||
{
|
||||
public:
|
||||
glShader(const Ref<Assets::TextAsset> &vertex_asset, const Ref<Assets::TextAsset> &pixel_asset);
|
||||
|
||||
~glShader() override;
|
||||
|
||||
void bind() override;
|
||||
|
||||
void un_bind() override;
|
||||
|
||||
private:
|
||||
unsigned int compile_shader(const std::string &source, Shader::Stage stage);
|
||||
|
||||
unsigned int m_shader_id { 0u };
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include <renderer/shared_context.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class glSharedContext: public SharedContext
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
#include <asset_parser/assets/texture.hpp>
|
||||
#include <lt_debug/assertions.hpp>
|
||||
#include <renderer/gl/texture.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
glTexture::glTexture(const Ref<Assets::TextureAsset> &asset)
|
||||
{
|
||||
const auto metadata = asset->get_metadata();
|
||||
const auto blob_metadata = asset->get_blob_metadata(Assets::BlobMetadata::Tag::color);
|
||||
|
||||
glCreateTextures(GL_TEXTURE_2D, 1, &m_texture_id);
|
||||
glTextureParameteri(m_texture_id, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTextureParameteri(m_texture_id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTextureParameteri(m_texture_id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTextureParameteri(m_texture_id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
auto blob = std::vector<std::byte>(blob_metadata.uncompressed_size);
|
||||
asset->unpack_blob(blob_metadata.tag, blob.data(), blob.size());
|
||||
|
||||
bind();
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
map_num_components_to_internal_format(metadata.num_components),
|
||||
static_cast<int>(metadata.pixel_size[0]),
|
||||
static_cast<int>(metadata.pixel_size[1]),
|
||||
0,
|
||||
map_num_components_to_format(metadata.num_components),
|
||||
GL_UNSIGNED_BYTE,
|
||||
std::bit_cast<unsigned char *>(blob.data())
|
||||
);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
glTexture::~glTexture()
|
||||
{
|
||||
glDeleteTextures(1, &m_texture_id);
|
||||
}
|
||||
|
||||
void glTexture::bind(unsigned int slot /* = 0u */)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + slot);
|
||||
glBindTexture(GL_TEXTURE_2D, m_texture_id);
|
||||
}
|
||||
|
||||
auto glTexture::get_texture() -> void *
|
||||
{
|
||||
return (void *)(intptr_t)m_texture_id;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto glTexture::map_num_components_to_format(uint32_t num_components) const -> int
|
||||
{
|
||||
switch (num_components)
|
||||
{
|
||||
case 4u: return GL_RGBA;
|
||||
case 3u: return GL_RGB;
|
||||
case 2u: return GL_RG;
|
||||
case 1u: return GL_RED;
|
||||
default: ensure(false, "Invalid number of components: {}", num_components);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] auto glTexture::map_num_components_to_internal_format(uint32_t num_components) const
|
||||
-> int
|
||||
{
|
||||
switch (num_components)
|
||||
{
|
||||
case 4u: return GL_RGBA8;
|
||||
case 3u: return GL_RGB8;
|
||||
case 2u: return GL_RG8;
|
||||
case 1u: return GL_R8;
|
||||
default: ensure(false, "Invalid number of components: {}", num_components);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <renderer/texture.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class glTexture: public Texture
|
||||
{
|
||||
public:
|
||||
glTexture(const Ref<Assets::TextureAsset> &asset);
|
||||
|
||||
~glTexture() override;
|
||||
|
||||
void bind(unsigned int slot = 0u) override;
|
||||
|
||||
auto get_texture() -> void * override;
|
||||
|
||||
private:
|
||||
[[nodiscard]] auto map_num_components_to_format(uint32_t num_components) const -> int;
|
||||
|
||||
[[nodiscard]] auto map_num_components_to_internal_format(uint32_t num_components) const -> int;
|
||||
|
||||
uint32_t m_texture_id {};
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1 +0,0 @@
|
|||
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include <ui/ui.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class glUserInterface: public UserInterface
|
||||
{
|
||||
public:
|
||||
glUserInterface() = default;
|
||||
|
||||
~glUserInterface() override;
|
||||
|
||||
void platform_implementation(Ref<SharedContext> sharedContext) override;
|
||||
|
||||
void begin() override;
|
||||
|
||||
void end() override;
|
||||
|
||||
void log_debug_data() override;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
#include <lt_debug/assertions.hpp>
|
||||
#include <renderer/gl/buffers.hpp>
|
||||
#include <renderer/gl/vertex_layout.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
glVertexLayout::glVertexLayout(
|
||||
const Ref<VertexBuffer> &buffer,
|
||||
const std::vector<std::pair<std::string, VertexElementType>> &elements
|
||||
)
|
||||
: m_array_id()
|
||||
{
|
||||
// check
|
||||
ensure(
|
||||
std::dynamic_pointer_cast<glVertexBuffer>(buffer),
|
||||
"Failed to cast 'VertexBuffer' to 'glVertexBuffer'"
|
||||
);
|
||||
ensure(!elements.empty(), "'elements' is empty");
|
||||
|
||||
// local
|
||||
auto elementsDesc = std::vector<glVertexElementDesc> {};
|
||||
elementsDesc.reserve(elements.size());
|
||||
auto stride = 0u;
|
||||
|
||||
// extract elements desc
|
||||
for (const auto &element : elements)
|
||||
{
|
||||
elementsDesc.push_back(get_element_desc(element.second, stride));
|
||||
stride += elementsDesc.back().typeSize * elementsDesc.back().count;
|
||||
}
|
||||
|
||||
// create vertex array
|
||||
glCreateVertexArrays(1, &m_array_id);
|
||||
|
||||
// bind buffer and array
|
||||
buffer->bind();
|
||||
bind();
|
||||
|
||||
// enable vertex attributes
|
||||
auto index = 0u;
|
||||
for (const auto &elementDesc : elementsDesc)
|
||||
{
|
||||
glVertexAttribPointer(
|
||||
index,
|
||||
elementDesc.count,
|
||||
elementDesc.type,
|
||||
GL_FALSE,
|
||||
stride,
|
||||
(const void *)elementDesc.offset
|
||||
);
|
||||
glEnableVertexAttribArray(index++);
|
||||
}
|
||||
}
|
||||
|
||||
glVertexLayout::~glVertexLayout()
|
||||
{
|
||||
glDeleteVertexArrays(1, &m_array_id);
|
||||
}
|
||||
|
||||
void glVertexLayout::bind()
|
||||
{
|
||||
glBindVertexArray(m_array_id);
|
||||
}
|
||||
|
||||
void glVertexLayout::un_bind()
|
||||
{
|
||||
glBindVertexArray({});
|
||||
}
|
||||
|
||||
auto glVertexLayout::get_element_desc(VertexElementType type, unsigned int offset)
|
||||
-> glVertexElementDesc
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
/* byte */
|
||||
case lt::VertexElementType::Byte1:
|
||||
return { .type = GL_BYTE, .count = 1u, .typeSize = sizeof(GLbyte), .offset = offset };
|
||||
case lt::VertexElementType::Byte2:
|
||||
return { .type = GL_BYTE, .count = 1u, .typeSize = sizeof(GLbyte), .offset = offset };
|
||||
case lt::VertexElementType::Byte4:
|
||||
return { .type = GL_BYTE, .count = 1u, .typeSize = sizeof(GLbyte), .offset = offset };
|
||||
|
||||
/* ubyte */
|
||||
case lt::VertexElementType::UByte1:
|
||||
return { .type = GL_UNSIGNED_BYTE,
|
||||
.count = 1u,
|
||||
.typeSize = sizeof(GLubyte),
|
||||
.offset = offset };
|
||||
case lt::VertexElementType::UByte2:
|
||||
return { .type = GL_UNSIGNED_BYTE,
|
||||
.count = 2u,
|
||||
.typeSize = sizeof(GLubyte),
|
||||
.offset = offset };
|
||||
case lt::VertexElementType::UByte4:
|
||||
return { .type = GL_UNSIGNED_BYTE,
|
||||
.count = 4u,
|
||||
.typeSize = sizeof(GLubyte),
|
||||
.offset = offset };
|
||||
|
||||
/* int */
|
||||
case VertexElementType::Int1:
|
||||
return { .type = GL_INT, .count = 1u, .typeSize = sizeof(GLint), .offset = offset };
|
||||
case VertexElementType::Int2:
|
||||
return { .type = GL_INT, .count = 2u, .typeSize = sizeof(GLint), .offset = offset };
|
||||
case VertexElementType::Int3:
|
||||
return { .type = GL_INT, .count = 3u, .typeSize = sizeof(GLint), .offset = offset };
|
||||
case VertexElementType::Int4:
|
||||
return { .type = GL_INT, .count = 4u, .typeSize = sizeof(GLint), .offset = offset };
|
||||
|
||||
/* uint */
|
||||
case VertexElementType::UInt1:
|
||||
return { .type = GL_UNSIGNED_INT,
|
||||
.count = 1u,
|
||||
.typeSize = sizeof(GLuint),
|
||||
.offset = offset };
|
||||
case VertexElementType::UInt2:
|
||||
return { .type = GL_UNSIGNED_INT,
|
||||
.count = 2u,
|
||||
.typeSize = sizeof(GLuint),
|
||||
.offset = offset };
|
||||
case VertexElementType::UInt3:
|
||||
return { .type = GL_UNSIGNED_INT,
|
||||
.count = 3u,
|
||||
.typeSize = sizeof(GLuint),
|
||||
.offset = offset };
|
||||
case VertexElementType::UInt4:
|
||||
return { .type = GL_UNSIGNED_INT,
|
||||
.count = 4u,
|
||||
.typeSize = sizeof(GLuint),
|
||||
.offset = offset };
|
||||
|
||||
/* float */
|
||||
case VertexElementType::Float1:
|
||||
return { .type = GL_FLOAT, .count = 1u, .typeSize = sizeof(GLfloat), .offset = offset };
|
||||
case VertexElementType::Float2:
|
||||
return { .type = GL_FLOAT, .count = 2u, .typeSize = sizeof(GLfloat), .offset = offset };
|
||||
case VertexElementType::Float3:
|
||||
return { .type = GL_FLOAT, .count = 3u, .typeSize = sizeof(GLfloat), .offset = offset };
|
||||
case VertexElementType::Float4:
|
||||
return { .type = GL_FLOAT, .count = 4u, .typeSize = sizeof(GLfloat), .offset = offset };
|
||||
|
||||
default: ensure(false, "Invalid 'VertexElementType'"); return {};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include <renderer/vertex_layout.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class VertexBuffer;
|
||||
|
||||
struct glVertexElementDesc
|
||||
{
|
||||
unsigned int type;
|
||||
|
||||
unsigned int count;
|
||||
|
||||
unsigned int typeSize;
|
||||
|
||||
unsigned int offset;
|
||||
};
|
||||
|
||||
class glVertexLayout: public VertexLayout
|
||||
{
|
||||
public:
|
||||
glVertexLayout(
|
||||
const Ref<VertexBuffer> &buffer,
|
||||
const std::vector<std::pair<std::string, VertexElementType>> &elements
|
||||
);
|
||||
|
||||
~glVertexLayout() override;
|
||||
|
||||
void bind() override;
|
||||
|
||||
void un_bind() override;
|
||||
|
||||
private:
|
||||
auto get_element_desc(VertexElementType type, unsigned int offset) -> glVertexElementDesc;
|
||||
|
||||
unsigned int m_array_id;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
#include <renderer/gl/graphics_context.hpp>
|
||||
#include <renderer/graphics_context.hpp>
|
||||
|
||||
#ifdef LIGHT_PLATFORM_WINDOWS
|
||||
#include <renderer/dx/graphics_context.hpp>
|
||||
#include <renderer/dx/shared_context.hpp>
|
||||
#endif
|
||||
|
||||
namespace lt {
|
||||
|
||||
GraphicsContext *GraphicsContext::s_context = nullptr;
|
||||
|
||||
GraphicsContext::~GraphicsContext() = default;
|
||||
|
||||
auto GraphicsContext::create(GraphicsAPI api) -> Scope<GraphicsContext>
|
||||
{
|
||||
delete s_context;
|
||||
|
||||
if (api == GraphicsAPI::Default)
|
||||
{
|
||||
#if defined(LIGHT_PLATFORM_WINDOWS)
|
||||
api = GraphicsAPI::DirectX;
|
||||
#elif defined(LIGHT_PLATFORM_LINUX)
|
||||
api = GraphicsAPI::OpenGL;
|
||||
#elif defined(LIGHT_PLATFORM_MAC)
|
||||
api = GraphicsAPI::OpenGL;
|
||||
#endif
|
||||
}
|
||||
|
||||
auto scope_gfx = Scope<GraphicsContext> {};
|
||||
switch (api)
|
||||
{
|
||||
// opengl
|
||||
case GraphicsAPI::OpenGL:
|
||||
scope_gfx = create_scope<glGraphicsContext>(window_handle);
|
||||
s_context = scope_gfx.get();
|
||||
break;
|
||||
// directx
|
||||
case GraphicsAPI::DirectX:
|
||||
lt_win(
|
||||
scope_gfx = create_scope<dxGraphicsContext>(window_handle); s_context = scope_gfx.get();
|
||||
break;
|
||||
)
|
||||
|
||||
default
|
||||
: ensure(
|
||||
false,
|
||||
"Invalid/unsupported 'GraphicsAPI' {}",
|
||||
// TODO(Light): Stringifier::graphics_api_to_string(api),
|
||||
"TODO"
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::move(scope_gfx);
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
#include <camera/camera.hpp>
|
||||
#include <renderer/buffers.hpp>
|
||||
#include <renderer/programs/quad.hpp>
|
||||
#include <renderer/shader.hpp>
|
||||
#include <renderer/vertex_layout.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
QuadRendererProgram::QuadRendererProgram(
|
||||
unsigned int max_vertices,
|
||||
const Ref<SharedContext> &shared_context,
|
||||
Ref<Shader> shader
|
||||
)
|
||||
: m_index_buffer(nullptr)
|
||||
, m_vertex_layout(nullptr)
|
||||
, m_max_vertices(max_vertices)
|
||||
, m_shader(std::move(shader))
|
||||
{
|
||||
m_vertex_buffer = Ref<VertexBuffer>(
|
||||
VertexBuffer::create(nullptr, sizeof(QuadVertexData), max_vertices, shared_context)
|
||||
);
|
||||
m_index_buffer = Ref<IndexBuffer>(
|
||||
IndexBuffer::create(nullptr, (max_vertices / 4) * 6, shared_context)
|
||||
);
|
||||
m_vertex_layout = Ref<VertexLayout>(VertexLayout::create(
|
||||
m_vertex_buffer,
|
||||
m_shader,
|
||||
{ { "POSITION", VertexElementType::Float4 }, { "COLOR", VertexElementType::Float4 } },
|
||||
shared_context
|
||||
));
|
||||
}
|
||||
|
||||
auto QuadRendererProgram::advance() -> bool
|
||||
{
|
||||
m_idx += 4;
|
||||
if (m_idx >= m_map.size())
|
||||
{
|
||||
log_wrn("'VertexBuffer' map went beyond 'MaxVertices': {}", m_max_vertices);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_quad_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void QuadRendererProgram::map()
|
||||
{
|
||||
m_map = std::span<QuadVertexData> {
|
||||
static_cast<QuadRendererProgram::QuadVertexData *>(m_vertex_buffer->map()),
|
||||
m_max_vertices,
|
||||
};
|
||||
|
||||
m_quad_count = 0u;
|
||||
m_idx = {};
|
||||
}
|
||||
|
||||
void QuadRendererProgram::un_map()
|
||||
{
|
||||
m_vertex_buffer->un_map();
|
||||
m_idx = {};
|
||||
}
|
||||
|
||||
void QuadRendererProgram::bind()
|
||||
{
|
||||
m_shader->bind();
|
||||
m_vertex_layout->bind();
|
||||
m_vertex_buffer->bind();
|
||||
m_index_buffer->bind();
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <math/vec4.hpp>
|
||||
#include <renderer/programs/renderer_program.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class Shader;
|
||||
class VertexBuffer;
|
||||
class IndexBuffer;
|
||||
class VertexLayout;
|
||||
class OrthographicCamera;
|
||||
class SharedContext;
|
||||
class Shader;
|
||||
|
||||
class QuadRendererProgram: RendererProgram
|
||||
{
|
||||
public:
|
||||
~QuadRendererProgram() override = default;
|
||||
struct QuadVertexData
|
||||
{
|
||||
math::vec4 position;
|
||||
|
||||
math::vec4 tint;
|
||||
};
|
||||
|
||||
QuadRendererProgram(
|
||||
unsigned int max_vertices,
|
||||
const Ref<SharedContext> &shared_context,
|
||||
Ref<Shader> shader
|
||||
);
|
||||
|
||||
auto advance() -> bool;
|
||||
|
||||
void map() override;
|
||||
|
||||
void un_map() override;
|
||||
|
||||
void bind() override;
|
||||
|
||||
auto get_map_current() -> QuadVertexData *
|
||||
{
|
||||
return &m_map[m_idx];
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_quad_count() const -> unsigned int
|
||||
{
|
||||
return m_quad_count;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto get_vertex_size() const -> unsigned int
|
||||
{
|
||||
return sizeof(QuadVertexData);
|
||||
}
|
||||
|
||||
private:
|
||||
Ref<Shader> m_shader;
|
||||
|
||||
Ref<VertexBuffer> m_vertex_buffer;
|
||||
|
||||
Ref<IndexBuffer> m_index_buffer;
|
||||
|
||||
Ref<VertexLayout> m_vertex_layout;
|
||||
|
||||
std::span<QuadVertexData> m_map;
|
||||
|
||||
size_t m_idx {};
|
||||
|
||||
unsigned int m_quad_count = 0u;
|
||||
|
||||
unsigned int m_max_vertices = 0u;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
namespace lt {
|
||||
|
||||
class RendererProgram
|
||||
{
|
||||
virtual void map() = 0;
|
||||
|
||||
virtual void un_map() = 0;
|
||||
|
||||
virtual void bind() = 0;
|
||||
|
||||
public:
|
||||
virtual ~RendererProgram() = default;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
#include <camera/camera.hpp>
|
||||
#include <renderer/buffers.hpp>
|
||||
#include <renderer/programs/texture.hpp>
|
||||
#include <renderer/shader.hpp>
|
||||
#include <renderer/vertex_layout.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
TextureRendererProgram::TextureRendererProgram(
|
||||
unsigned int max_vertices,
|
||||
const Ref<SharedContext> &shared_context,
|
||||
Ref<Shader> shader
|
||||
)
|
||||
: m_shader(std::move(shader))
|
||||
, m_index_buffer(nullptr)
|
||||
, m_vertex_layout(nullptr)
|
||||
, m_max_vertices(max_vertices)
|
||||
{
|
||||
// #todo: don't use relative path
|
||||
// AssetManager::load_shader(
|
||||
// "LT_ENGINE_RESOURCES_TEXTURE_SHADER",
|
||||
// "data/assets/shaders/texture/vs.asset",
|
||||
// "data/assets/shaders/texture/ps.asset"
|
||||
// );
|
||||
//
|
||||
// AssetManager::load_shader(
|
||||
// "LT_ENGINE_RESOURCES_TINTED_TEXTURE_SHADER",
|
||||
// "data/assets/shaders/tinted_texture/vs.asset",
|
||||
// "data/assets/shaders/tinted_texture/ps.asset"
|
||||
// );
|
||||
//
|
||||
// AssetManager::load_shader(
|
||||
// "LT_ENGINE_RESOURCES_QUAD_SHADER",
|
||||
// "data/assets/shaders/quads/vs.asset",
|
||||
// "data/assets/shaders/quads/ps.asset"
|
||||
// );
|
||||
//
|
||||
// m_shader = AssetManager::get_shader("LT_ENGINE_RESOURCES_TEXTURE_SHADER");
|
||||
// m_shader = AssetManager::get_shader("LT_ENGINE_RESOURCES_TINTED_TEXTURE_SHADER");
|
||||
// m_shader = AssetManager::get_shader("LT_ENGINE_RESOURCES_QUAD_SHADER");
|
||||
|
||||
|
||||
m_vertex_buffer = Ref<VertexBuffer>(
|
||||
VertexBuffer::create(nullptr, sizeof(TextureVertexData), max_vertices, shared_context)
|
||||
);
|
||||
m_index_buffer = Ref<IndexBuffer>(
|
||||
IndexBuffer::create(nullptr, (max_vertices / 4) * 6, shared_context)
|
||||
);
|
||||
m_vertex_layout = Ref<VertexLayout>(VertexLayout::create(
|
||||
m_vertex_buffer,
|
||||
m_shader,
|
||||
{ { "POSITION", VertexElementType::Float4 }, { "TEXCOORD", VertexElementType::Float2 } },
|
||||
shared_context
|
||||
));
|
||||
}
|
||||
|
||||
auto TextureRendererProgram::advance() -> bool
|
||||
{
|
||||
m_idx += 4;
|
||||
if (m_idx >= m_map.size())
|
||||
{
|
||||
log_wrn("'VertexBuffer' map went beyond 'MaxVertices': {}", m_max_vertices);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_quad_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void TextureRendererProgram::map()
|
||||
{
|
||||
m_map = std::span<TextureVertexData> {
|
||||
static_cast<TextureVertexData *>(m_vertex_buffer->map()),
|
||||
m_max_vertices,
|
||||
};
|
||||
|
||||
m_quad_count = 0u;
|
||||
m_idx = {};
|
||||
}
|
||||
|
||||
void TextureRendererProgram::un_map()
|
||||
{
|
||||
m_vertex_buffer->un_map();
|
||||
}
|
||||
|
||||
void TextureRendererProgram::bind()
|
||||
{
|
||||
m_shader->bind();
|
||||
m_vertex_layout->bind();
|
||||
m_vertex_buffer->bind();
|
||||
m_index_buffer->bind();
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <math/vec2.hpp>
|
||||
#include <math/vec4.hpp>
|
||||
#include <renderer/programs/renderer_program.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class Shader;
|
||||
class VertexBuffer;
|
||||
class IndexBuffer;
|
||||
class VertexLayout;
|
||||
class OrthographicCamera;
|
||||
class SharedContext;
|
||||
|
||||
class TextureRendererProgram: RendererProgram
|
||||
{
|
||||
public:
|
||||
~TextureRendererProgram() override = default;
|
||||
|
||||
struct TextureVertexData
|
||||
{
|
||||
math::vec4 position;
|
||||
|
||||
math::vec2 texcoord;
|
||||
};
|
||||
|
||||
TextureRendererProgram(
|
||||
unsigned int max_vertices,
|
||||
const Ref<SharedContext> &shared_context,
|
||||
Ref<Shader> shader
|
||||
);
|
||||
|
||||
auto advance() -> bool;
|
||||
|
||||
void map() override;
|
||||
|
||||
void un_map() override;
|
||||
|
||||
void bind() override;
|
||||
|
||||
auto get_map_current() -> TextureVertexData *
|
||||
{
|
||||
return &m_map[m_idx];
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_quad_count() const -> unsigned int
|
||||
{
|
||||
return m_quad_count;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto get_vertex_size() const -> unsigned int
|
||||
{
|
||||
return sizeof(TextureVertexData);
|
||||
}
|
||||
|
||||
private:
|
||||
Ref<Shader> m_shader;
|
||||
|
||||
Ref<VertexBuffer> m_vertex_buffer;
|
||||
|
||||
Ref<IndexBuffer> m_index_buffer;
|
||||
|
||||
Ref<VertexLayout> m_vertex_layout;
|
||||
|
||||
std::span<TextureVertexData> m_map;
|
||||
|
||||
size_t m_idx {};
|
||||
|
||||
unsigned int m_quad_count { 0u };
|
||||
|
||||
unsigned int m_max_vertices;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
#include <camera/camera.hpp>
|
||||
#include <renderer/buffers.hpp>
|
||||
#include <renderer/programs/tinted_texture.hpp>
|
||||
#include <renderer/shader.hpp>
|
||||
#include <renderer/vertex_layout.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
TintedTextureRendererProgram::TintedTextureRendererProgram(
|
||||
unsigned int max_vertices,
|
||||
const Ref<SharedContext> &shared_context,
|
||||
Ref<Shader> shader
|
||||
|
||||
)
|
||||
: m_shader(std::move(shader))
|
||||
, m_index_buffer(nullptr)
|
||||
, m_vertex_layout(nullptr)
|
||||
, m_max_vertices(max_vertices)
|
||||
{
|
||||
m_vertex_buffer = Ref<VertexBuffer>(
|
||||
VertexBuffer::create(nullptr, sizeof(TintedTextureVertexData), max_vertices, shared_context)
|
||||
);
|
||||
m_index_buffer = Ref<IndexBuffer>(
|
||||
IndexBuffer::create(nullptr, (max_vertices / 4) * 6, shared_context)
|
||||
);
|
||||
m_vertex_layout = Ref<VertexLayout>(VertexLayout::create(
|
||||
m_vertex_buffer,
|
||||
m_shader,
|
||||
{ { "POSITION", VertexElementType::Float4 },
|
||||
{ "TINT", VertexElementType::Float4 },
|
||||
{ "TEXCOORD", VertexElementType::Float2 } },
|
||||
shared_context
|
||||
));
|
||||
}
|
||||
|
||||
auto TintedTextureRendererProgram::advance() -> bool
|
||||
{
|
||||
m_idx += 4;
|
||||
if (m_idx >= m_map.size())
|
||||
{
|
||||
log_wrn("'VertexBuffer' map went beyond 'MaxVertices': {}", m_max_vertices);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_quad_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void TintedTextureRendererProgram::map()
|
||||
{
|
||||
m_map = std::span<TintedTextureVertexData> {
|
||||
static_cast<TintedTextureVertexData *>(m_vertex_buffer->map()),
|
||||
m_max_vertices,
|
||||
};
|
||||
|
||||
m_quad_count = 0u;
|
||||
m_idx = {};
|
||||
}
|
||||
|
||||
void TintedTextureRendererProgram::un_map()
|
||||
{
|
||||
m_vertex_buffer->un_map();
|
||||
}
|
||||
|
||||
void TintedTextureRendererProgram::bind()
|
||||
{
|
||||
m_shader->bind();
|
||||
m_vertex_layout->bind();
|
||||
m_vertex_buffer->bind();
|
||||
m_index_buffer->bind();
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <math/vec2.hpp>
|
||||
#include <math/vec4.hpp>
|
||||
#include <renderer/programs/renderer_program.hpp>
|
||||
#include <span>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class Shader;
|
||||
class VertexBuffer;
|
||||
class IndexBuffer;
|
||||
class VertexLayout;
|
||||
class OrthographicCamera;
|
||||
class SharedContext;
|
||||
|
||||
class TintedTextureRendererProgram: RendererProgram
|
||||
{
|
||||
public:
|
||||
~TintedTextureRendererProgram() override = default;
|
||||
struct TintedTextureVertexData
|
||||
{
|
||||
math::vec4 position;
|
||||
|
||||
math::vec4 tint;
|
||||
|
||||
math::vec2 texcoord;
|
||||
};
|
||||
|
||||
TintedTextureRendererProgram(
|
||||
unsigned int max_vertices,
|
||||
const Ref<SharedContext> &shared_context,
|
||||
Ref<Shader> shader
|
||||
);
|
||||
|
||||
auto advance() -> bool;
|
||||
|
||||
void map() override;
|
||||
|
||||
void un_map() override;
|
||||
|
||||
void bind() override;
|
||||
|
||||
auto get_map_current() -> TintedTextureVertexData *
|
||||
{
|
||||
return &m_map[m_idx];
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_quad_count() const -> unsigned int
|
||||
{
|
||||
return m_quad_count;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto get_vertex_size() const -> unsigned int
|
||||
{
|
||||
return sizeof(TintedTextureVertexData);
|
||||
}
|
||||
|
||||
private:
|
||||
Ref<Shader> m_shader;
|
||||
|
||||
Ref<VertexBuffer> m_vertex_buffer;
|
||||
|
||||
Ref<IndexBuffer> m_index_buffer;
|
||||
|
||||
Ref<VertexLayout> m_vertex_layout;
|
||||
|
||||
std::span<TintedTextureVertexData> m_map;
|
||||
|
||||
size_t m_idx {};
|
||||
|
||||
unsigned int m_quad_count { 0u };
|
||||
|
||||
unsigned int m_max_vertices;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
#include <lt_debug/assertions.hpp>
|
||||
#include <renderer/gl/render_command.hpp>
|
||||
#include <renderer/render_command.hpp>
|
||||
|
||||
#ifdef LIGHT_PLATFORM_WINDOWS
|
||||
#include <renderer/dx/render_command.hpp>
|
||||
#include <renderer/dx/shared_context.hpp>
|
||||
#endif
|
||||
|
||||
#include <renderer/graphics_context.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
auto RenderCommand::create(const Ref<SharedContext> & /*sharedContext*/) -> Scope<RenderCommand>
|
||||
{
|
||||
switch (GraphicsContext::get_graphics_api())
|
||||
{
|
||||
case GraphicsAPI::OpenGL: return create_scope<glRenderCommand>();
|
||||
|
||||
case GraphicsAPI::DirectX:
|
||||
lt_win(
|
||||
return create_scope<dxRenderCommand>(
|
||||
(std::static_pointer_cast<dxSharedContext>)(sharedContext)
|
||||
);
|
||||
)
|
||||
|
||||
default
|
||||
: ensure(
|
||||
false,
|
||||
"Invalid/unsupported 'GraphicsAPI' {}",
|
||||
static_cast<uint32_t>(GraphicsContext::get_graphics_api())
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,330 +0,0 @@
|
|||
#include <ecs/registry.hpp>
|
||||
#include <input/events/window.hpp>
|
||||
#include <lt_debug/assertions.hpp>
|
||||
#include <renderer/blender.hpp>
|
||||
#include <renderer/buffers.hpp>
|
||||
#include <renderer/framebuffer.hpp>
|
||||
#include <renderer/programs/quad.hpp>
|
||||
#include <renderer/programs/texture.hpp>
|
||||
#include <renderer/programs/tinted_texture.hpp>
|
||||
#include <renderer/render_command.hpp>
|
||||
#include <renderer/renderer.hpp>
|
||||
#include <renderer/shader.hpp>
|
||||
#include <renderer/texture.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
Renderer *Renderer::s_context = nullptr;
|
||||
|
||||
Renderer::Renderer(const Ref<SharedContext> &shared_context, CreateInfo create_info)
|
||||
: m_quad_renderer(
|
||||
create_scope<QuadRendererProgram>(
|
||||
LT_MAX_QUAD_RENDERER_VERTICES,
|
||||
shared_context,
|
||||
std::move(create_info.quad_renderer_shader)
|
||||
)
|
||||
)
|
||||
, m_texture_renderer(
|
||||
create_scope<TextureRendererProgram>(
|
||||
LT_MAX_TEXTURE_RENDERER_VERTICES,
|
||||
shared_context,
|
||||
std::move(create_info.texture_renderer_shader)
|
||||
)
|
||||
)
|
||||
, m_tinted_texture_renderer(
|
||||
create_scope<TintedTextureRendererProgram>(
|
||||
LT_MAX_TINTED_TEXTURE_RENDERER_VERTICES,
|
||||
shared_context,
|
||||
std::move(create_info.tinted_texture_renderer_shader)
|
||||
)
|
||||
)
|
||||
, m_view_projection_buffer(nullptr)
|
||||
, m_render_command(nullptr)
|
||||
, m_blender(nullptr)
|
||||
, m_target_framebuffer(nullptr)
|
||||
|
||||
{
|
||||
ensure(!s_context, "An instance of 'renderer' already exists, do not construct this class!");
|
||||
s_context = this;
|
||||
|
||||
m_view_projection_buffer = ConstantBuffer::create(
|
||||
ConstantBufferIndex::ViewProjection,
|
||||
sizeof(math::mat4),
|
||||
shared_context
|
||||
);
|
||||
|
||||
m_render_command = RenderCommand::create(window_handle, shared_context);
|
||||
m_blender = Blender::create(shared_context);
|
||||
m_blender->enable(BlendFactor::SRC_ALPHA, BlendFactor::INVERSE_SRC_ALPHA);
|
||||
}
|
||||
|
||||
Renderer::~Renderer() // NOLINT
|
||||
{
|
||||
}
|
||||
|
||||
auto Renderer::create(Ref<SharedContext> sharedContext, CreateInfo create_info) -> Scope<Renderer>
|
||||
{
|
||||
return make_scope<Renderer>(new Renderer(std::move(sharedContext), std::move(create_info)));
|
||||
}
|
||||
|
||||
void Renderer::on_window_resize(const WindowResizedEvent &event)
|
||||
{
|
||||
m_render_command->set_viewport(0u, 0u, event.get_size().x, event.get_size().y);
|
||||
}
|
||||
|
||||
//======================================== DRAW_QUAD ========================================//
|
||||
/* tinted textures */
|
||||
void Renderer::draw_quad_impl(
|
||||
const math::vec3 &position,
|
||||
const math::vec2 &size,
|
||||
const math::vec4 &tint,
|
||||
Ref<Texture> texture
|
||||
)
|
||||
{
|
||||
draw_quad(
|
||||
math::translate(position) * math::scale(math::vec3 { size.x, size.y, 1.0f }),
|
||||
tint,
|
||||
texture
|
||||
);
|
||||
}
|
||||
|
||||
/* tint */
|
||||
void Renderer::draw_quad_impl(
|
||||
const math::vec3 &position,
|
||||
const math::vec2 &size,
|
||||
const math::vec4 &tint
|
||||
)
|
||||
{
|
||||
draw_quad(math::translate(position) * math::scale(math::vec3 { size.x, size.y, 1.0f }), tint);
|
||||
}
|
||||
|
||||
/* texture */
|
||||
void Renderer::draw_quad_impl(
|
||||
const math::vec3 &position,
|
||||
const math::vec2 &size,
|
||||
Ref<Texture> texture
|
||||
)
|
||||
{
|
||||
draw_quad(
|
||||
math::translate(position) * math::scale(math::vec3 { size.x, size.y, 1.0f }),
|
||||
texture
|
||||
);
|
||||
}
|
||||
//======================================== DRAW_QUAD ========================================//
|
||||
|
||||
//==================== DRAW_QUAD_TINT ====================//
|
||||
void Renderer::draw_quad_impl(const math::mat4 &transform, const math::vec4 &tint)
|
||||
{
|
||||
auto map = std::span<QuadRendererProgram::QuadVertexData> { m_quad_renderer->get_map_current(),
|
||||
4 };
|
||||
|
||||
// top left
|
||||
map[0].position = transform * math::vec4(-0.5f, -0.5f, 0.0f, 1.0f);
|
||||
map[0].tint = tint;
|
||||
|
||||
// top right
|
||||
map[1].position = transform * math::vec4(0.5f, -0.5f, 0.0f, 1.0f);
|
||||
map[1].tint = tint;
|
||||
|
||||
// bottom right
|
||||
map[2].position = transform * math::vec4(0.5f, 0.5f, 0.0f, 1.0f);
|
||||
map[2].tint = tint;
|
||||
|
||||
// bottom left
|
||||
map[3].position = transform * math::vec4(-0.5f, 0.5f, 0.0f, 1.0f);
|
||||
map[3].tint = tint;
|
||||
|
||||
// advance
|
||||
if (!m_quad_renderer->advance())
|
||||
{
|
||||
log_wrn("Exceeded LT_MAX_QUAD_RENDERER_VERTICES: {}", LT_MAX_QUAD_RENDERER_VERTICES);
|
||||
flush_scene();
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::draw_quad_impl(const math::mat4 &transform, const Ref<Texture> &texture)
|
||||
{
|
||||
ensure(texture, "Texture passed to renderer::draw_quad_impl");
|
||||
|
||||
texture->bind();
|
||||
auto map = std::span<TextureRendererProgram::TextureVertexData> {
|
||||
m_texture_renderer->get_map_current(),
|
||||
4
|
||||
};
|
||||
|
||||
// top left
|
||||
map[0].position = transform * math::vec4(-0.5f, -0.5f, 0.0f, 1.0f);
|
||||
map[0].texcoord = { 0.0f, 0.0f };
|
||||
|
||||
// top right
|
||||
map[1].position = transform * math::vec4(0.5f, -0.5f, 0.0f, 1.0f);
|
||||
map[1].texcoord = { 1.0f, 0.0f };
|
||||
|
||||
// bottom right
|
||||
map[2].position = transform * math::vec4(0.5f, 0.5f, 0.0f, 1.0f);
|
||||
map[2].texcoord = { 1.0f, 1.0f };
|
||||
|
||||
// bottom left
|
||||
map[3].position = transform * math::vec4(-0.5f, 0.5f, 0.0f, 1.0f);
|
||||
map[3].texcoord = { 0.0f, 1.0f };
|
||||
|
||||
// advance
|
||||
if (!m_texture_renderer->advance())
|
||||
{
|
||||
log_wrn("Exceeded LT_MAX_TEXTURE_RENDERER_VERTICES: {}", LT_MAX_TEXTURE_RENDERER_VERTICES);
|
||||
flush_scene();
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::draw_quad_impl(
|
||||
const math::mat4 &transform,
|
||||
const math::vec4 &tint,
|
||||
const Ref<Texture> &texture
|
||||
)
|
||||
{
|
||||
ensure(texture, "Texture passed to renderer::draw_quad_impl");
|
||||
|
||||
texture->bind();
|
||||
auto map = std::span<TintedTextureRendererProgram::TintedTextureVertexData> {
|
||||
m_tinted_texture_renderer->get_map_current(),
|
||||
4
|
||||
};
|
||||
|
||||
// top left
|
||||
map[0].position = transform * math::vec4(-0.5f, -0.5f, 0.0f, 1.0f);
|
||||
map[0].tint = tint;
|
||||
map[0].texcoord = { 0.0f, 0.0f };
|
||||
|
||||
// top right
|
||||
map[1].position = transform * math::vec4(0.5f, -0.5f, 0.0f, 1.0f);
|
||||
map[1].tint = tint;
|
||||
map[1].texcoord = { 1.0f, 0.0f };
|
||||
|
||||
// bottom right
|
||||
map[2].position = transform * math::vec4(0.5f, 0.5f, 0.0f, 1.0f);
|
||||
map[2].tint = tint;
|
||||
map[2].texcoord = { 1.0f, 1.0f };
|
||||
|
||||
// bottom left
|
||||
map[3].position = transform * math::vec4(-0.5f, 0.5f, 0.0f, 1.0f);
|
||||
map[3].tint = tint;
|
||||
map[3].texcoord = { 0.0f, 1.0f };
|
||||
|
||||
if (!m_tinted_texture_renderer->advance())
|
||||
{
|
||||
log_wrn("Exceeded LT_MAX_TEXTURE_RENDERER_VERTICES: {}", LT_MAX_TEXTURE_RENDERER_VERTICES);
|
||||
flush_scene();
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::begin_frame()
|
||||
{
|
||||
}
|
||||
|
||||
void Renderer::end_frame()
|
||||
{
|
||||
m_render_command->swap_buffers();
|
||||
m_render_command->clear_back_buffer(
|
||||
m_default_framebuffer_camera ? m_default_framebuffer_camera->get_background_color() :
|
||||
math::vec4(0.0f)
|
||||
);
|
||||
|
||||
m_default_framebuffer_camera = nullptr;
|
||||
}
|
||||
|
||||
void Renderer::begin_scene_impl(
|
||||
Camera *camera,
|
||||
const math::mat4 &cameraTransform,
|
||||
const Ref<Framebuffer> &targetFrameBuffer /* = nullptr */
|
||||
)
|
||||
{
|
||||
// determine the target frame buffer
|
||||
m_target_framebuffer = targetFrameBuffer;
|
||||
|
||||
if (targetFrameBuffer)
|
||||
{
|
||||
targetFrameBuffer->bind_as_target(camera->get_background_color());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_default_framebuffer_camera = camera;
|
||||
m_render_command->default_target_framebuffer();
|
||||
}
|
||||
|
||||
// update view projection buffer
|
||||
auto *map = (math::mat4 *)m_view_projection_buffer->map();
|
||||
map[0] = camera->get_projection() * math::inverse(cameraTransform);
|
||||
m_view_projection_buffer->un_map();
|
||||
|
||||
// map renderers
|
||||
m_quad_renderer->map();
|
||||
m_texture_renderer->map();
|
||||
m_tinted_texture_renderer->map();
|
||||
}
|
||||
|
||||
void Renderer::flush_scene()
|
||||
{
|
||||
/* tinted texture renderer */
|
||||
m_tinted_texture_renderer->un_map();
|
||||
if (m_tinted_texture_renderer->get_quad_count())
|
||||
{
|
||||
m_tinted_texture_renderer->bind();
|
||||
m_render_command->draw_indexed(m_tinted_texture_renderer->get_quad_count() * 6u);
|
||||
}
|
||||
|
||||
/* quad renderer */
|
||||
m_quad_renderer->un_map();
|
||||
if (m_quad_renderer->get_quad_count())
|
||||
{
|
||||
m_quad_renderer->bind();
|
||||
m_render_command->draw_indexed(m_quad_renderer->get_quad_count() * 6u);
|
||||
}
|
||||
|
||||
/* texture renderer */
|
||||
m_texture_renderer->un_map();
|
||||
if (m_texture_renderer->get_quad_count())
|
||||
{
|
||||
m_texture_renderer->bind();
|
||||
m_render_command->draw_indexed(m_texture_renderer->get_quad_count() * 6u);
|
||||
}
|
||||
|
||||
m_quad_renderer->map();
|
||||
m_texture_renderer->map();
|
||||
m_tinted_texture_renderer->map();
|
||||
}
|
||||
|
||||
void Renderer::end_scene_impl()
|
||||
{
|
||||
/* tinted texture renderer */
|
||||
m_tinted_texture_renderer->un_map();
|
||||
if (m_tinted_texture_renderer->get_quad_count())
|
||||
{
|
||||
m_tinted_texture_renderer->bind();
|
||||
m_render_command->draw_indexed(m_tinted_texture_renderer->get_quad_count() * 6u);
|
||||
}
|
||||
|
||||
/* quad renderer */
|
||||
m_quad_renderer->un_map();
|
||||
if (m_quad_renderer->get_quad_count())
|
||||
{
|
||||
m_quad_renderer->bind();
|
||||
m_render_command->draw_indexed(m_quad_renderer->get_quad_count() * 6u);
|
||||
}
|
||||
|
||||
/* texture renderer */
|
||||
m_texture_renderer->un_map();
|
||||
if (m_texture_renderer->get_quad_count())
|
||||
{
|
||||
m_texture_renderer->bind();
|
||||
m_render_command->draw_indexed(m_texture_renderer->get_quad_count() * 6u);
|
||||
}
|
||||
|
||||
// reset frame buffer
|
||||
if (m_target_framebuffer)
|
||||
{
|
||||
m_target_framebuffer = nullptr;
|
||||
m_render_command->default_target_framebuffer();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
#include <asset_parser/assets/text.hpp>
|
||||
#include <renderer/gl/shader.hpp>
|
||||
#include <renderer/shader.hpp>
|
||||
|
||||
#ifdef LIGHT_PLATFORM_WINDOWS
|
||||
#include <renderer/dx/shader.hpp>
|
||||
#include <renderer/dx/shared_context.hpp>
|
||||
#endif
|
||||
|
||||
#include <renderer/graphics_context.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
/* static */ auto Shader::create(
|
||||
const Ref<Assets::TextAsset> &vertex_asset,
|
||||
const Ref<Assets::TextAsset> &pixel_asset,
|
||||
const Ref<SharedContext> &shared_context
|
||||
) -> Ref<Shader>
|
||||
{
|
||||
std::ignore = shared_context;
|
||||
|
||||
// load shader source
|
||||
switch (GraphicsContext::get_graphics_api())
|
||||
{
|
||||
case GraphicsAPI::OpenGL:
|
||||
return create_ref<glShader>(std::move(vertex_asset), std::move(pixel_asset));
|
||||
|
||||
case GraphicsAPI::DirectX:
|
||||
lt_win(
|
||||
return create_ref<dxShader>(
|
||||
vertex_asset,
|
||||
pixel_asset,
|
||||
std::static_pointer_cast<dxSharedContext>(sharedContext)
|
||||
);
|
||||
);
|
||||
|
||||
default:
|
||||
ensure(
|
||||
false,
|
||||
"Invalid/unsupported 'GraphicsAPI' {}",
|
||||
static_cast<uint32_t>(GraphicsContext::get_graphics_api())
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
#include <lt_debug/assertions.hpp>
|
||||
#include <renderer/system.hpp>
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
System::System(InitRequirements requirements): m_registry(std::move(requirements.registry))
|
||||
{
|
||||
ensure(m_registry, "null registry");
|
||||
}
|
||||
|
||||
System::~System() = default;
|
||||
|
||||
void System::tick(TickRequirements requirements)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace lt::renderer
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
#include <ranges>
|
||||
#include <renderer/system.hpp>
|
||||
#include <test/test.hpp>
|
||||
#include <window/window.hpp>
|
||||
|
||||
using namespace lt;
|
||||
|
||||
using lt::test::Case;
|
||||
using lt::test::Suite;
|
||||
|
||||
Suite raii = [] {
|
||||
using lt::test::expect_true;
|
||||
using lt::test::expect_throw;
|
||||
using renderer::System;
|
||||
|
||||
Case { "happy" } = [=] {
|
||||
std::ignore = System { {
|
||||
.registry = create_ref<ecs::Registry>(),
|
||||
} };
|
||||
};
|
||||
|
||||
Case { "unhappy" } = [=] {
|
||||
expect_throw([=] {
|
||||
std::ignore = System { {
|
||||
.registry = {},
|
||||
} };
|
||||
});
|
||||
|
||||
expect_throw([=] {
|
||||
std::ignore = System { {
|
||||
.registry = create_ref<ecs::Registry>(),
|
||||
} };
|
||||
});
|
||||
};
|
||||
|
||||
Case { "plenty" } = [=] {
|
||||
for (auto idx : std::views::iota(0, 100'001))
|
||||
{
|
||||
std::ignore = System { {
|
||||
.registry = create_ref<ecs::Registry>(),
|
||||
} };
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
#include <logger/logger.hpp>
|
||||
#include <renderer/gl/texture.hpp>
|
||||
#include <renderer/texture.hpp>
|
||||
|
||||
#ifdef LIGHT_PLATFORM_WINDOWS
|
||||
#include <renderer/dx/shared_context.hpp>
|
||||
#include <renderer/dx/texture.hpp>
|
||||
#endif
|
||||
|
||||
#include <renderer/graphics_context.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
/* static */ auto Texture::create(
|
||||
const Ref<Assets::TextureAsset> &asset,
|
||||
const Ref<SharedContext> & /*shared_context*/
|
||||
) -> Ref<Texture>
|
||||
{
|
||||
switch (GraphicsContext::get_graphics_api())
|
||||
{
|
||||
case GraphicsAPI::OpenGL: return create_ref<glTexture>(std::move(asset));
|
||||
|
||||
case GraphicsAPI::DirectX:
|
||||
lt_win(
|
||||
return create_ref<dxTexture>(
|
||||
width,
|
||||
height,
|
||||
components,
|
||||
pixels,
|
||||
std::static_pointer_cast<dxSharedContext>(sharedContext),
|
||||
filePath
|
||||
);
|
||||
)
|
||||
|
||||
default
|
||||
: ensure(
|
||||
false,
|
||||
"Invalid/unsupported 'GraphicsAPI' {}",
|
||||
static_cast<uint32_t>(GraphicsContext::get_graphics_api())
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
#include <lt_debug/assertions.hpp>
|
||||
#include <renderer/gl/vertex_layout.hpp>
|
||||
#include <renderer/vertex_layout.hpp>
|
||||
|
||||
#ifdef LIGHT_PLATFORM_WINDOWS
|
||||
#include <renderer/dx/shared_context.hpp>
|
||||
#include <renderer/dx/vertex_layout.hpp>
|
||||
#endif
|
||||
|
||||
#include <renderer/graphics_context.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
auto VertexLayout::create(
|
||||
const Ref<VertexBuffer> &vertexBuffer,
|
||||
const Ref<Shader> & /*shader*/,
|
||||
const std::vector<std::pair<std::string, VertexElementType>> &elements,
|
||||
const Ref<SharedContext> & /*sharedContext*/
|
||||
) -> Ref<VertexLayout>
|
||||
{
|
||||
switch (GraphicsContext::get_graphics_api())
|
||||
{
|
||||
case GraphicsAPI::OpenGL: return create_ref<glVertexLayout>(vertexBuffer, elements);
|
||||
|
||||
case GraphicsAPI::DirectX:
|
||||
lt_win(
|
||||
return create_ref<dxVertexLayout>(
|
||||
shader,
|
||||
elements,
|
||||
std::static_pointer_cast<dxSharedContext>(sharedContext)
|
||||
);
|
||||
)
|
||||
|
||||
default
|
||||
: ensure(
|
||||
false,
|
||||
"Invalid/unsupported 'GraphicsAPI' {}",
|
||||
static_cast<uint32_t>(GraphicsContext::get_graphics_api())
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
#include <renderer/vk/instance.hpp>
|
||||
|
||||
namespace lt::vk {
|
||||
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace lt::vk {
|
||||
|
||||
class Instance
|
||||
{
|
||||
public:
|
||||
Instance()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
::vk::Instance m_instace;
|
||||
};
|
||||
|
||||
} // namespace lt::vk
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#include <renderer/vk/instance.hpp>
|
||||
#include <test/test.hpp>
|
||||
|
||||
lt::test::Suite raii = [] {
|
||||
lt::test::Case { "raii" } = [] {
|
||||
auto instance = lt::vk::Instance {};
|
||||
};
|
||||
};
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
class Backend
|
||||
{
|
||||
public:
|
||||
enum class API : uint8_t
|
||||
{
|
||||
vulkan,
|
||||
directx,
|
||||
};
|
||||
|
||||
Backend() = default;
|
||||
|
||||
Backend(Backend &&) = default;
|
||||
|
||||
auto operator=(Backend &&) -> Backend & = default;
|
||||
|
||||
Backend(const Backend &) = delete;
|
||||
|
||||
auto operator=(const Backend &) -> Backend & = delete;
|
||||
|
||||
virtual ~Backend() = default;
|
||||
|
||||
[[nodiscard]] virtual auto get_api() const -> API = 0;
|
||||
};
|
||||
|
||||
} // namespace lt::renderer
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
namespace lt {
|
||||
|
||||
class SharedContext;
|
||||
|
||||
enum class BlendFactor : uint8_t
|
||||
{
|
||||
// constants
|
||||
ZERO,
|
||||
ONE,
|
||||
|
||||
// source
|
||||
SRC_COLOR,
|
||||
INVERSE_SRC_COLOR,
|
||||
|
||||
SRC_ALPHA,
|
||||
INVERSE_SRC_ALPHA,
|
||||
|
||||
// destination
|
||||
DST_COLOR,
|
||||
INVERSE_DST_COLOR,
|
||||
|
||||
DST_ALPHA,
|
||||
INVERSE_DST_ALPHA,
|
||||
|
||||
// source1
|
||||
SRC1_COLOR,
|
||||
INVERSE_SRC1_COLOR,
|
||||
|
||||
SRC1_ALPHA,
|
||||
INVERSE_SRC1_ALPHA,
|
||||
};
|
||||
|
||||
class Blender
|
||||
{
|
||||
public:
|
||||
virtual ~Blender() = default;
|
||||
static auto create(const Ref<SharedContext> &sharedContext) -> Scope<Blender>;
|
||||
|
||||
virtual void enable(BlendFactor srcFactor, BlendFactor dstFactor) = 0;
|
||||
|
||||
virtual void disable() = 0;
|
||||
|
||||
protected:
|
||||
Blender() = default;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
namespace lt {
|
||||
|
||||
class SharedContext;
|
||||
|
||||
enum class ConstantBufferIndex
|
||||
{
|
||||
ViewProjection = 0u
|
||||
};
|
||||
|
||||
class ConstantBuffer
|
||||
{
|
||||
public:
|
||||
virtual ~ConstantBuffer() = default;
|
||||
static auto create(
|
||||
ConstantBufferIndex index,
|
||||
unsigned int size,
|
||||
const Ref<SharedContext> &sharedContext
|
||||
) -> Scope<ConstantBuffer>;
|
||||
|
||||
virtual auto map() -> void * = 0;
|
||||
|
||||
virtual void un_map() = 0;
|
||||
|
||||
virtual void bind() = 0;
|
||||
|
||||
protected:
|
||||
ConstantBuffer() = default;
|
||||
};
|
||||
|
||||
class VertexBuffer
|
||||
{
|
||||
public:
|
||||
static auto create(
|
||||
float *vertices,
|
||||
unsigned int stride,
|
||||
unsigned int count,
|
||||
const Ref<SharedContext> &sharedContext
|
||||
) -> Ref<VertexBuffer>;
|
||||
|
||||
virtual ~VertexBuffer() = default;
|
||||
|
||||
virtual auto map() -> void * = 0;
|
||||
|
||||
virtual void un_map() = 0;
|
||||
|
||||
virtual void bind() = 0;
|
||||
|
||||
virtual void un_bind() = 0;
|
||||
|
||||
protected:
|
||||
VertexBuffer() = default;
|
||||
};
|
||||
|
||||
class IndexBuffer
|
||||
{
|
||||
public:
|
||||
static auto create(
|
||||
unsigned int *indices,
|
||||
unsigned int count,
|
||||
const Ref<SharedContext> &sharedContext
|
||||
) -> Ref<IndexBuffer>;
|
||||
|
||||
virtual ~IndexBuffer() = default;
|
||||
|
||||
virtual void bind() = 0;
|
||||
|
||||
virtual void un_bind() = 0;
|
||||
|
||||
protected:
|
||||
IndexBuffer() = default;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
/** Requires a Transform Component
|
||||
* @todo(Light): Figure out how to enforce a component requirement list for a component
|
||||
*/
|
||||
struct RendererComponent
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace lt::renderer
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
class Context
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace lt::renderer
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <math/vec2.hpp>
|
||||
#include <math/vec4.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class SharedContext;
|
||||
|
||||
struct FramebufferSpecification
|
||||
{
|
||||
unsigned int width {};
|
||||
|
||||
unsigned int height {};
|
||||
|
||||
unsigned int samples = 1;
|
||||
};
|
||||
|
||||
class Framebuffer
|
||||
{
|
||||
public:
|
||||
virtual ~Framebuffer() = default;
|
||||
static auto create(
|
||||
const FramebufferSpecification &specification,
|
||||
const Ref<SharedContext> &sharedContext
|
||||
) -> Ref<Framebuffer>;
|
||||
|
||||
virtual void bind_as_target(const math::vec4 &clearColor) = 0;
|
||||
|
||||
virtual void bind_as_resource() = 0;
|
||||
|
||||
virtual void resize(const math::uvec2 &size) = 0;
|
||||
|
||||
virtual auto get_color_attachment() -> void * = 0;
|
||||
|
||||
protected:
|
||||
Framebuffer() = default;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
namespace lt {
|
||||
|
||||
class SharedContext;
|
||||
class WindowResizedEvent;
|
||||
|
||||
enum class GraphicsAPI
|
||||
{
|
||||
Default = 0,
|
||||
OpenGL,
|
||||
DirectX,
|
||||
Vulkan,
|
||||
Metal
|
||||
};
|
||||
|
||||
class GraphicsContext
|
||||
{
|
||||
public:
|
||||
static auto create(GraphicsAPI api) -> Scope<GraphicsContext>;
|
||||
GraphicsContext(const GraphicsContext &) = delete;
|
||||
|
||||
GraphicsContext &operator=(const GraphicsContext &) = delete;
|
||||
|
||||
virtual ~GraphicsContext();
|
||||
|
||||
virtual void log_debug_data() = 0;
|
||||
|
||||
static GraphicsAPI get_graphics_api()
|
||||
{
|
||||
return s_context->m_graphics_api;
|
||||
}
|
||||
|
||||
static Ref<SharedContext> get_shared_context()
|
||||
{
|
||||
return s_context->m_shared_context;
|
||||
}
|
||||
|
||||
protected:
|
||||
GraphicsContext() = default;
|
||||
|
||||
GraphicsAPI m_graphics_api;
|
||||
|
||||
Ref<SharedContext> m_shared_context = nullptr;
|
||||
|
||||
private:
|
||||
static GraphicsContext *s_context;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <math/vec4.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class SharedContext;
|
||||
|
||||
class RenderCommand
|
||||
{
|
||||
public:
|
||||
static auto create(const Ref<SharedContext> &sharedContext) -> Scope<RenderCommand>;
|
||||
|
||||
RenderCommand(const RenderCommand &) = delete;
|
||||
|
||||
auto operator=(const RenderCommand &) -> RenderCommand & = delete;
|
||||
|
||||
virtual ~RenderCommand() = default;
|
||||
|
||||
virtual void swap_buffers() = 0;
|
||||
|
||||
virtual void clear_back_buffer(const math::vec4 &clearColor) = 0;
|
||||
|
||||
virtual void draw(unsigned int count) = 0;
|
||||
|
||||
virtual void draw_indexed(unsigned int count) = 0;
|
||||
|
||||
virtual void default_target_framebuffer() = 0;
|
||||
|
||||
virtual void set_viewport(
|
||||
unsigned int x,
|
||||
unsigned int y,
|
||||
unsigned int width,
|
||||
unsigned int height
|
||||
) = 0;
|
||||
|
||||
protected:
|
||||
RenderCommand() = default;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <math/mat4.hpp>
|
||||
#include <math/vec3.hpp>
|
||||
#include <math/vec4.hpp>
|
||||
#include <renderer/blender.hpp>
|
||||
#include <renderer/buffers.hpp>
|
||||
#include <renderer/render_command.hpp>
|
||||
#include <renderer/renderer.hpp>
|
||||
#include <utility>
|
||||
|
||||
#define LT_MAX_QUAD_RENDERER_VERTICES (1028u * 4u)
|
||||
#define LT_MAX_TEXTURE_RENDERER_VERTICES (1028u * 4u)
|
||||
#define LT_MAX_TINTED_TEXTURE_RENDERER_VERTICES (1028u * 4u)
|
||||
|
||||
namespace lt {
|
||||
|
||||
class ConstantBuffer;
|
||||
class Framebuffer;
|
||||
class RenderCommand;
|
||||
class Texture;
|
||||
class SharedContext;
|
||||
class Camera;
|
||||
class WindowResizedEvent;
|
||||
class Shader;
|
||||
|
||||
class TintedTextureRendererProgram;
|
||||
class QuadRendererProgram;
|
||||
class TextureRendererProgram;
|
||||
|
||||
class Renderer
|
||||
{
|
||||
public:
|
||||
struct CreateInfo
|
||||
{
|
||||
Ref<Shader> quad_renderer_shader;
|
||||
|
||||
Ref<Shader> texture_renderer_shader;
|
||||
|
||||
Ref<Shader> tinted_texture_renderer_shader;
|
||||
};
|
||||
|
||||
static auto create(Ref<SharedContext> sharedContext, CreateInfo create_info) -> Scope<Renderer>;
|
||||
|
||||
static void draw_quad(
|
||||
const math::vec3 &position,
|
||||
const math::vec2 &size,
|
||||
const math::vec4 &tint,
|
||||
Ref<Texture> texture
|
||||
)
|
||||
{
|
||||
s_context->draw_quad_impl(position, size, tint, std::move(texture));
|
||||
}
|
||||
|
||||
static void draw_quad(
|
||||
const math::vec3 &position,
|
||||
const math::vec2 &size,
|
||||
const math::vec4 &tint
|
||||
)
|
||||
{
|
||||
s_context->draw_quad_impl(position, size, tint);
|
||||
}
|
||||
|
||||
static void draw_quad(const math::vec3 &position, const math::vec2 &size, Ref<Texture> texture)
|
||||
{
|
||||
s_context->draw_quad_impl(position, size, std::move(texture));
|
||||
}
|
||||
|
||||
static void draw_quad(
|
||||
const math::mat4 &transform,
|
||||
const math::vec4 &tint,
|
||||
const Ref<Texture> &texture
|
||||
)
|
||||
{
|
||||
s_context->draw_quad_impl(transform, tint, texture);
|
||||
}
|
||||
|
||||
static void draw_quad(const math::mat4 &transform, const math::vec4 &tint)
|
||||
{
|
||||
s_context->draw_quad_impl(transform, tint);
|
||||
}
|
||||
|
||||
static void draw_quad(const math::mat4 &transform, const Ref<Texture> &texture)
|
||||
{
|
||||
s_context->draw_quad_impl(transform, texture);
|
||||
}
|
||||
|
||||
static void begin_scene(
|
||||
Camera *camera,
|
||||
const math::mat4 &cameraTransform,
|
||||
const Ref<Framebuffer> &targetFrameBuffer = nullptr
|
||||
)
|
||||
{
|
||||
s_context->begin_scene_impl(camera, cameraTransform, targetFrameBuffer);
|
||||
}
|
||||
|
||||
static void end_scene()
|
||||
{
|
||||
s_context->end_scene_impl();
|
||||
}
|
||||
|
||||
~Renderer();
|
||||
|
||||
void on_window_resize(const WindowResizedEvent &event);
|
||||
|
||||
void begin_frame();
|
||||
|
||||
void end_frame();
|
||||
|
||||
private:
|
||||
static Renderer *s_context;
|
||||
|
||||
Scope<QuadRendererProgram> m_quad_renderer;
|
||||
|
||||
Scope<TextureRendererProgram> m_texture_renderer;
|
||||
|
||||
Scope<TintedTextureRendererProgram> m_tinted_texture_renderer;
|
||||
|
||||
Scope<ConstantBuffer> m_view_projection_buffer;
|
||||
|
||||
Scope<RenderCommand> m_render_command;
|
||||
|
||||
Scope<Blender> m_blender;
|
||||
|
||||
Camera *m_default_framebuffer_camera { nullptr };
|
||||
|
||||
Ref<Framebuffer> m_target_framebuffer;
|
||||
|
||||
bool m_should_clear_backbuffer { false };
|
||||
|
||||
Renderer(const Ref<SharedContext> &shared_context, CreateInfo create_info);
|
||||
|
||||
void draw_quad_impl(
|
||||
const math::vec3 &position,
|
||||
const math::vec2 &size,
|
||||
const math::vec4 &tint,
|
||||
Ref<Texture> texture
|
||||
);
|
||||
|
||||
void draw_quad_impl(const math::vec3 &position, const math::vec2 &size, const math::vec4 &tint);
|
||||
|
||||
void draw_quad_impl(const math::vec3 &position, const math::vec2 &size, Ref<Texture> texture);
|
||||
|
||||
void draw_quad_impl(
|
||||
const math::mat4 &transform,
|
||||
const math::vec4 &tint,
|
||||
const Ref<Texture> &texture
|
||||
);
|
||||
|
||||
void draw_quad_impl(const math::mat4 &transform, const math::vec4 &tint);
|
||||
|
||||
void draw_quad_impl(const math::mat4 &transform, const Ref<Texture> &texture);
|
||||
|
||||
void begin_scene_impl(
|
||||
Camera *camera,
|
||||
const math::mat4 &cameraTransform,
|
||||
const Ref<Framebuffer> &targetFrameBuffer = nullptr
|
||||
);
|
||||
|
||||
void flush_scene();
|
||||
|
||||
void end_scene_impl();
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
namespace Assets {
|
||||
|
||||
class TextAsset;
|
||||
|
||||
} // namespace Assets
|
||||
|
||||
namespace lt {
|
||||
|
||||
class SharedContext;
|
||||
|
||||
class Shader
|
||||
{
|
||||
public:
|
||||
enum Stage : uint8_t
|
||||
{
|
||||
none = 0,
|
||||
|
||||
vertex,
|
||||
pixel,
|
||||
geometry,
|
||||
};
|
||||
|
||||
static auto create(
|
||||
const Ref<Assets::TextAsset> &vertex_asset,
|
||||
const Ref<Assets::TextAsset> &pixel_asset,
|
||||
const Ref<SharedContext> &shared_context
|
||||
) -> Ref<Shader>;
|
||||
|
||||
virtual ~Shader() = default;
|
||||
|
||||
virtual void bind() = 0;
|
||||
|
||||
virtual void un_bind() = 0;
|
||||
|
||||
protected:
|
||||
Shader() = default;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
namespace lt {
|
||||
|
||||
class SharedContext
|
||||
{
|
||||
public:
|
||||
virtual ~SharedContext() = default;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <base/base.hpp>
|
||||
#include <ecs/registry.hpp>
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
/** The system for putting gore on your display
|
||||
*
|
||||
* Exclusively operates on components:
|
||||
* - RendererComponent
|
||||
* - PostEffectsComponent
|
||||
* - UserInterfaceComponent
|
||||
*
|
||||
* Requires read acces on components:
|
||||
* - TransformComponent
|
||||
*/
|
||||
class System
|
||||
{
|
||||
public:
|
||||
/** The configurations of this system. */
|
||||
struct Properties
|
||||
{
|
||||
};
|
||||
|
||||
/** The requirements for this system to initialize. */
|
||||
struct InitRequirements
|
||||
{
|
||||
Ref<ecs::Registry> registry;
|
||||
};
|
||||
|
||||
/** The requirements for this system to tick. */
|
||||
struct TickRequirements
|
||||
{
|
||||
double delta_time;
|
||||
};
|
||||
|
||||
[[nodiscard]] System(InitRequirements requirements);
|
||||
|
||||
System(System &&) = default;
|
||||
|
||||
System(const System &) = delete;
|
||||
|
||||
auto operator=(System &&) -> System & = default;
|
||||
|
||||
auto operator=(const System &) -> System & = delete;
|
||||
|
||||
~System();
|
||||
|
||||
void tick(TickRequirements requirements);
|
||||
|
||||
private:
|
||||
Ref<ecs::Registry> m_registry;
|
||||
};
|
||||
|
||||
} // namespace lt::renderer
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
namespace Assets {
|
||||
|
||||
class TextureAsset;
|
||||
}
|
||||
|
||||
namespace lt {
|
||||
|
||||
class SharedContext;
|
||||
|
||||
class Texture
|
||||
{
|
||||
public:
|
||||
static Ref<Texture> create(
|
||||
const Ref<Assets::TextureAsset> &asset,
|
||||
const Ref<SharedContext> &shared_context
|
||||
);
|
||||
|
||||
virtual ~Texture() = default;
|
||||
|
||||
Texture(Texture &&) = default;
|
||||
|
||||
auto operator=(Texture &&) -> Texture & = default;
|
||||
|
||||
Texture(const Texture &) = delete;
|
||||
|
||||
auto operator=(const Texture &) -> Texture & = delete;
|
||||
|
||||
virtual void bind(unsigned int slot = 0) = 0;
|
||||
|
||||
virtual auto get_texture() -> void * = 0;
|
||||
|
||||
protected:
|
||||
Texture() = default;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <lt_debug/assertions.hpp>
|
||||
|
||||
namespace lt {
|
||||
|
||||
class VertexBuffer;
|
||||
class Shader;
|
||||
class SharedContext;
|
||||
|
||||
enum class VertexElementType
|
||||
{
|
||||
Byte1,
|
||||
Byte2,
|
||||
Byte4,
|
||||
UByte1,
|
||||
UByte2,
|
||||
UByte4,
|
||||
Int1,
|
||||
Int2,
|
||||
Int3,
|
||||
Int4,
|
||||
UInt1,
|
||||
UInt2,
|
||||
UInt3,
|
||||
UInt4,
|
||||
Float1,
|
||||
Float2,
|
||||
Float3,
|
||||
Float4,
|
||||
};
|
||||
|
||||
class VertexLayout
|
||||
{
|
||||
public:
|
||||
static auto create(
|
||||
const Ref<VertexBuffer> &vertexBuffer,
|
||||
const Ref<Shader> &shader,
|
||||
const std::vector<std::pair<std::string, VertexElementType>> &elements,
|
||||
const Ref<SharedContext> &sharedContext
|
||||
) -> Ref<VertexLayout>;
|
||||
|
||||
virtual ~VertexLayout() = default;
|
||||
|
||||
virtual void bind() = 0;
|
||||
|
||||
virtual void un_bind() = 0;
|
||||
|
||||
protected:
|
||||
VertexLayout() = default;
|
||||
};
|
||||
|
||||
} // namespace lt
|
||||
|
|
@ -69,29 +69,73 @@ struct RendererContext
|
|||
};
|
||||
}
|
||||
|
||||
Suite raii = [] {
|
||||
class SystemTest
|
||||
{
|
||||
public:
|
||||
SystemTest()
|
||||
{
|
||||
m_surface_entity->add<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {
|
||||
.title = "",
|
||||
.resolution = resolution,
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] auto registry() const -> Ref<ecs::Registry>
|
||||
{
|
||||
return m_registry;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto surface_entity() const -> const ecs::Entity &
|
||||
{
|
||||
return *m_surface_entity;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto stats() const -> Ref<app::SystemStats>
|
||||
{
|
||||
return m_stats;
|
||||
}
|
||||
|
||||
private:
|
||||
Ref<app::SystemStats> m_stats = create_ref<app::SystemStats>();
|
||||
|
||||
Ref<ecs::Registry> m_registry = create_ref<ecs::Registry>();
|
||||
|
||||
Ref<surface::System> m_surface_system = create_ref<surface::System>(m_registry);
|
||||
|
||||
Scope<ecs::Entity> m_surface_entity = create_scope<ecs::Entity>(
|
||||
m_registry,
|
||||
m_registry->create_entity()
|
||||
);
|
||||
};
|
||||
|
||||
Suite raii = "raii"_suite = [] {
|
||||
Case { "happy path won't throw" } = [&] {
|
||||
ignore = create_system();
|
||||
};
|
||||
|
||||
Case { "happy path has no validation errors" } = [&] {
|
||||
auto [surface, renderer] = create_system();
|
||||
expect_true(renderer.system.get_stats().empty_diagnosis());
|
||||
auto fixture = SystemTest {};
|
||||
std::ignore = System(
|
||||
{
|
||||
.registry = fixture.registry(),
|
||||
.surface_entity = fixture.surface_entity(),
|
||||
.system_stats = fixture.stats(),
|
||||
}
|
||||
);
|
||||
|
||||
expect_true(fixture.stats()->empty_diagnosis());
|
||||
};
|
||||
|
||||
Case { "unhappy path throws" } = [] {
|
||||
auto [surface_system, surface_entity] = create_surface();
|
||||
auto empty_registry = create_ref<ecs::Registry>();
|
||||
auto empty_entity = ecs::Entity { empty_registry, empty_registry->create_entity() };
|
||||
auto registry = create_ref<ecs::Registry>();
|
||||
auto stats = create_ref<app::SystemStats>();
|
||||
auto fixture = SystemTest {};
|
||||
auto empty_entity = ecs::Entity { fixture.registry(), fixture.registry()->create_entity() };
|
||||
|
||||
expect_throw([&] {
|
||||
ignore = System(
|
||||
{
|
||||
.registry = {},
|
||||
.surface_entity = surface_entity,
|
||||
.system_stats = stats,
|
||||
.surface_entity = fixture.surface_entity(),
|
||||
.system_stats = fixture.stats(),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
@ -99,9 +143,9 @@ Suite raii = [] {
|
|||
expect_throw([&] {
|
||||
ignore = System(
|
||||
System::CreateInfo {
|
||||
.registry = surface_entity.get_registry(),
|
||||
.registry = fixture.registry(),
|
||||
.surface_entity = empty_entity,
|
||||
.system_stats = stats,
|
||||
.system_stats = fixture.stats(),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
@ -109,8 +153,8 @@ Suite raii = [] {
|
|||
expect_throw([&] {
|
||||
ignore = System(
|
||||
System::CreateInfo {
|
||||
.registry = surface_entity.get_registry(),
|
||||
.surface_entity = surface_entity,
|
||||
.registry = fixture.registry(),
|
||||
.surface_entity = fixture.surface_entity(),
|
||||
.system_stats = {},
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,694 +0,0 @@
|
|||
#include <ranges>
|
||||
#include <renderer/vk/context.hpp>
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#elif defined(__unix__)
|
||||
#include <dlfcn.h>
|
||||
namespace {
|
||||
void *library = nullptr; // NOLINT
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
// global functions
|
||||
PFN_vkGetInstanceProcAddr vk_get_instance_proc_address;
|
||||
PFN_vkCreateInstance vk_create_instance;
|
||||
PFN_vkEnumerateInstanceExtensionProperties vk_enumerate_instance_extension_properties;
|
||||
PFN_vkEnumerateInstanceLayerProperties vk_enumerate_instance_layer_properties;
|
||||
|
||||
// instance functions
|
||||
PFN_vkDestroyInstance vk_destroy_instance;
|
||||
PFN_vkEnumeratePhysicalDevices vk_enumerate_physical_devices;
|
||||
PFN_vkGetPhysicalDeviceProperties vk_get_physical_device_properties;
|
||||
PFN_vkGetPhysicalDeviceQueueFamilyProperties vk_get_physical_device_queue_family_properties;
|
||||
PFN_vkCreateDevice vk_create_device;
|
||||
PFN_vkGetDeviceProcAddr vk_get_device_proc_address;
|
||||
PFN_vkDestroyDevice vk_destroy_device;
|
||||
PFN_vkGetPhysicalDeviceFeatures vk_get_physical_device_features;
|
||||
PFN_vkEnumerateDeviceExtensionProperties vk_enumerate_device_extension_properties;
|
||||
|
||||
// extension instance functions
|
||||
PFN_vkCmdBeginDebugUtilsLabelEXT vk_cmd_begin_debug_label;
|
||||
PFN_vkCmdEndDebugUtilsLabelEXT vk_cmd_end_debug_label;
|
||||
PFN_vkCmdInsertDebugUtilsLabelEXT vk_cmd_insert_debug_label;
|
||||
PFN_vkCreateDebugUtilsMessengerEXT vk_create_debug_messenger;
|
||||
PFN_vkDestroyDebugUtilsMessengerEXT vk_destroy_debug_messenger;
|
||||
PFN_vkQueueBeginDebugUtilsLabelEXT vk_queue_begin_debug_label;
|
||||
PFN_vkQueueEndDebugUtilsLabelEXT vk_queue_end_debug_label;
|
||||
PFN_vkQueueInsertDebugUtilsLabelEXT vk_queue_insert_debug_label;
|
||||
PFN_vkSetDebugUtilsObjectNameEXT vk_set_debug_object_name;
|
||||
PFN_vkSetDebugUtilsObjectTagEXT vk_set_debug_object_tag;
|
||||
PFN_vkSubmitDebugUtilsMessageEXT vk_submit_debug_message;
|
||||
|
||||
// device functions
|
||||
PFN_vkGetDeviceQueue vk_get_device_queue;
|
||||
PFN_vkCreateCommandPool vk_create_command_pool;
|
||||
PFN_vkDestroyCommandPool vk_destroy_command_pool;
|
||||
PFN_vkAllocateCommandBuffers vk_allocate_command_buffers;
|
||||
PFN_vkFreeCommandBuffers vk_free_command_buffers;
|
||||
PFN_vkBeginCommandBuffer vk_begin_command_buffer;
|
||||
PFN_vkEndCommandBuffer vk_end_command_buffer;
|
||||
PFN_vkCmdPipelineBarrier vk_cmd_pipeline_barrier;
|
||||
PFN_vkQueueSubmit vk_queue_submit;
|
||||
PFN_vkQueueWaitIdle vk_queue_wait_idle;
|
||||
PFN_vkDeviceWaitIdle vk_device_wait_idle;
|
||||
PFN_vkCreateFence vk_create_fence;
|
||||
PFN_vkDestroyFence vk_destroy_fence;
|
||||
PFN_vkWaitForFences vk_wait_for_fences;
|
||||
PFN_vkResetFences vk_reset_fences;
|
||||
PFN_vkCreateSemaphore vk_create_semaphore;
|
||||
PFN_vkDestroySemaphore vk_destroy_semaphore;
|
||||
PFN_vkCreateSwapchainKHR vk_create_swapchain_khr;
|
||||
PFN_vkDestroySwapchainKHR vk_destroy_swapchain_khr;
|
||||
PFN_vkGetSwapchainImagesKHR vk_get_swapchain_images_khr;
|
||||
PFN_vkAcquireNextImageKHR vk_acquire_next_image_khr;
|
||||
PFN_vkQueuePresentKHR vk_queue_present_khr;
|
||||
PFN_vkCreateImageView vk_create_image_view;
|
||||
PFN_vkDestroyImageView vk_destroy_image_view;
|
||||
PFN_vkCreateRenderPass vk_create_render_pass;
|
||||
PFN_vkDestroyRenderPass vk_destroy_render_pass;
|
||||
PFN_vkCreateFramebuffer vk_create_frame_buffer;
|
||||
PFN_vkDestroyFramebuffer vk_destroy_frame_buffer;
|
||||
PFN_vkCreateShaderModule vk_create_shader_module;
|
||||
PFN_vkDestroyShaderModule vk_destroy_shader_module;
|
||||
PFN_vkCreatePipelineLayout vk_create_pipeline_layout;
|
||||
PFN_vkDestroyPipelineLayout vk_destroy_pipeline_layout;
|
||||
PFN_vkCreateGraphicsPipelines vk_create_graphics_pipelines;
|
||||
PFN_vkDestroyPipeline vk_destroy_pipeline;
|
||||
PFN_vkCmdBeginRenderPass vk_cmd_begin_render_pass;
|
||||
PFN_vkCmdEndRenderPass vk_cmd_end_render_pass;
|
||||
PFN_vkCmdBindPipeline vk_cmd_bind_pipeline;
|
||||
PFN_vkCmdDraw vk_cmd_draw;
|
||||
PFN_vkCmdSetViewport vk_cmd_set_viewport;
|
||||
PFN_vkCmdSetScissor vk_cmd_set_scissors;
|
||||
|
||||
PFN_vkGetPhysicalDeviceSurfaceSupportKHR vk_get_physical_device_surface_support;
|
||||
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vk_get_physical_device_surface_capabilities;
|
||||
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vk_get_physical_device_surface_formats;
|
||||
|
||||
PFN_vkCreateXlibSurfaceKHR vk_create_xlib_surface_khr;
|
||||
PFN_vkDestroySurfaceKHR vk_destroy_surface_khr;
|
||||
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
auto parse_message_type(VkDebugUtilsMessageTypeFlagsEXT message_types) -> const char *;
|
||||
|
||||
auto parse_message_severity(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity)
|
||||
-> app::SystemDiagnosis::Severity;
|
||||
|
||||
auto validation_layers_callback(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT const message_severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT const message_types,
|
||||
VkDebugUtilsMessengerCallbackDataEXT const *const callback_data,
|
||||
void *const vulkan_user_data
|
||||
) -> VkBool32;
|
||||
|
||||
|
||||
Context::Context(const ecs::Entity &surface_entity, Ref<app::SystemStats> system_stats)
|
||||
: m_stats(std::move(system_stats))
|
||||
{
|
||||
ensure(m_stats, "Failed to create Vulkan Context: null stats");
|
||||
|
||||
load_library();
|
||||
load_global_functions();
|
||||
|
||||
initialize_instance();
|
||||
load_instance_functions();
|
||||
|
||||
initialize_debug_messenger();
|
||||
|
||||
initialize_physical_device();
|
||||
initialize_logical_device();
|
||||
load_device_functions();
|
||||
|
||||
initialize_surface(surface_entity);
|
||||
initialize_queue();
|
||||
initialize_swapchain();
|
||||
}
|
||||
|
||||
Context::~Context()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (m_device)
|
||||
{
|
||||
vkc(vk_device_wait_idle(m_device));
|
||||
|
||||
for (auto &view : m_swapchain_image_views)
|
||||
{
|
||||
vk_destroy_image_view(m_device, view, nullptr);
|
||||
}
|
||||
|
||||
vk_destroy_swapchain_khr(m_device, m_swapchain, nullptr);
|
||||
vk_destroy_device(m_device, nullptr);
|
||||
}
|
||||
|
||||
if (m_instance)
|
||||
{
|
||||
vk_destroy_surface_khr(m_instance, m_surface, nullptr);
|
||||
vk_destroy_debug_messenger(m_instance, m_debug_messenger, nullptr);
|
||||
// TODO(Light): fix this issue
|
||||
// @ref:
|
||||
// https://git.light7734.com/light7734/light/commit/f268724034a2ceb63b90dc13aedf86a1eecac62e
|
||||
// vk_destroy_instance(m_instance, nullptr);
|
||||
}
|
||||
}
|
||||
catch (const std::exception &exp)
|
||||
{
|
||||
log_err("Exception: {}", exp.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Context::initialize_instance()
|
||||
{
|
||||
auto app_info = VkApplicationInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||
.pApplicationName = "Hallo Hallo Hallo :3",
|
||||
.applicationVersion = VK_MAKE_VERSION(1, 4, 0),
|
||||
.pEngineName = "light",
|
||||
.engineVersion = VK_MAKE_VERSION(1, 4, 0),
|
||||
.apiVersion = VK_API_VERSION_1_4,
|
||||
};
|
||||
|
||||
auto extensions = std::vector<const char *> {
|
||||
VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
|
||||
VK_KHR_SURFACE_EXTENSION_NAME,
|
||||
VK_KHR_XLIB_SURFACE_EXTENSION_NAME,
|
||||
};
|
||||
auto layers = std::vector<const char *> {
|
||||
"VK_LAYER_KHRONOS_validation",
|
||||
};
|
||||
|
||||
auto instance_info = VkInstanceCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||||
.pApplicationInfo = &app_info,
|
||||
.enabledLayerCount = static_cast<uint32_t>(layers.size()),
|
||||
.ppEnabledLayerNames = layers.data(),
|
||||
.enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
|
||||
.ppEnabledExtensionNames = extensions.data(),
|
||||
};
|
||||
|
||||
{
|
||||
auto count = 0u;
|
||||
vkc(vk_enumerate_instance_extension_properties(nullptr, &count, nullptr));
|
||||
|
||||
auto extensions = std::vector<VkExtensionProperties>(count);
|
||||
vkc(vk_enumerate_instance_extension_properties(nullptr, &count, extensions.data()));
|
||||
|
||||
// log_inf("Available vulkan instance extensions:");
|
||||
for (auto &ext : extensions)
|
||||
{
|
||||
// log_inf("\t{} @ {}", ext.extensionName, ext.specVersion);
|
||||
}
|
||||
}
|
||||
|
||||
vkc(vk_create_instance(&instance_info, nullptr, &m_instance));
|
||||
|
||||
ensure(m_instance, "Failed to create vulkan instance");
|
||||
}
|
||||
|
||||
void Context::initialize_debug_messenger()
|
||||
{
|
||||
const auto info = VkDebugUtilsMessengerCreateInfoEXT {
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
|
||||
|
||||
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT
|
||||
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
|
||||
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
|
||||
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
|
||||
|
||||
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
|
||||
| VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
|
||||
| VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
|
||||
|
||||
|
||||
.pfnUserCallback = &validation_layers_callback,
|
||||
|
||||
.pUserData = &m_stats,
|
||||
};
|
||||
|
||||
ensure(
|
||||
!vk_create_debug_messenger(m_instance, &info, nullptr, &m_debug_messenger),
|
||||
"Failed to create vulkan debug utils messenger"
|
||||
);
|
||||
}
|
||||
|
||||
void Context::initialize_physical_device()
|
||||
{
|
||||
auto count = 0u;
|
||||
vkc(vk_enumerate_physical_devices(m_instance, &count, nullptr));
|
||||
ensure(count != 0u, "Failed to find any physical devices with Vulkan support");
|
||||
|
||||
auto devices = std::vector<VkPhysicalDevice>(count);
|
||||
vkc(vk_enumerate_physical_devices(m_instance, &count, devices.data()));
|
||||
|
||||
for (auto &device : devices)
|
||||
{
|
||||
auto properties = VkPhysicalDeviceProperties {};
|
||||
auto features = VkPhysicalDeviceFeatures {};
|
||||
|
||||
vk_get_physical_device_properties(device, &properties);
|
||||
vk_get_physical_device_features(device, &features);
|
||||
|
||||
if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
|
||||
&& features.geometryShader)
|
||||
{
|
||||
m_physical_device = device;
|
||||
}
|
||||
}
|
||||
ensure(m_physical_device, "Failed to find any suitable Vulkan physical device");
|
||||
}
|
||||
|
||||
void Context::initialize_logical_device()
|
||||
{
|
||||
const float priorities = .0f;
|
||||
|
||||
auto queue_info = VkDeviceQueueCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||
.queueFamilyIndex = find_suitable_queue_family(),
|
||||
.queueCount = 1u,
|
||||
.pQueuePriorities = &priorities,
|
||||
};
|
||||
|
||||
auto physical_device_features = VkPhysicalDeviceFeatures {};
|
||||
|
||||
auto extensions = std::vector<const char *> {
|
||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||
};
|
||||
|
||||
auto device_info = VkDeviceCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||
.queueCreateInfoCount = 1,
|
||||
.pQueueCreateInfos = &queue_info,
|
||||
.enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
|
||||
.ppEnabledExtensionNames = extensions.data(),
|
||||
.pEnabledFeatures = &physical_device_features,
|
||||
};
|
||||
|
||||
ensuree
|
||||
!vk_create_device(m_physical_device, &device_info, nullptr, &m_device),
|
||||
"Failed to create logical vulkan device"
|
||||
);
|
||||
}
|
||||
|
||||
void Context::initialize_surface(const ecs::Entity &surface_entity)
|
||||
{
|
||||
const auto &component = surface_entity.get<surface::SurfaceComponent>();
|
||||
|
||||
auto create_info = VkXlibSurfaceCreateInfoKHR {
|
||||
.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
|
||||
.dpy = component.get_native_data().display,
|
||||
.window = component.get_native_data().window,
|
||||
};
|
||||
|
||||
vkc(vk_create_xlib_surface_khr(m_instance, &create_info, nullptr, &m_surface));
|
||||
|
||||
const auto &[width, height] = component.get_resolution();
|
||||
m_framebuffer_size = {
|
||||
.width = width,
|
||||
.height = height,
|
||||
};
|
||||
}
|
||||
|
||||
void Context::initialize_queue()
|
||||
{
|
||||
vk_get_device_queue(m_device, find_suitable_queue_family(), 0, &m_queue);
|
||||
|
||||
auto count = uint32_t { 0u };
|
||||
vk_get_physical_device_queue_family_properties(m_physical_device, &count, nullptr);
|
||||
|
||||
auto properties = std::vector<VkQueueFamilyProperties>(count);
|
||||
vk_get_physical_device_queue_family_properties(m_physical_device, &count, properties.data());
|
||||
|
||||
for (auto idx = uint32_t { 0u }; const auto &property : properties)
|
||||
{
|
||||
if (property.queueFlags & VK_QUEUE_GRAPHICS_BIT)
|
||||
{
|
||||
m_graphics_queue_family_index = idx;
|
||||
}
|
||||
|
||||
auto has_presentation_support = VkBool32 { false };
|
||||
vkc(vk_get_physical_device_surface_support(
|
||||
m_physical_device,
|
||||
idx,
|
||||
m_surface,
|
||||
&has_presentation_support
|
||||
));
|
||||
if (has_presentation_support)
|
||||
{
|
||||
m_present_queue_family_index = idx;
|
||||
}
|
||||
|
||||
++idx;
|
||||
|
||||
if (m_graphics_queue_family_index != VK_QUEUE_FAMILY_IGNORED
|
||||
&& m_present_queue_family_index != VK_QUEUE_FAMILY_IGNORED)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ensure(
|
||||
m_graphics_queue_family_index != VK_QUEUE_FAMILY_IGNORED,
|
||||
"Failed to find graphics queue family"
|
||||
);
|
||||
|
||||
ensure(
|
||||
m_present_queue_family_index != VK_QUEUE_FAMILY_IGNORED,
|
||||
"Failed to find presentation queue family"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void Context::initialize_swapchain()
|
||||
{
|
||||
auto capabilities = VkSurfaceCapabilitiesKHR {};
|
||||
vkc(vk_get_physical_device_surface_capabilities(m_physical_device, m_surface, &capabilities));
|
||||
|
||||
auto count = uint32_t { 0 };
|
||||
vkc(vk_get_physical_device_surface_formats(m_physical_device, m_surface, &count, nullptr));
|
||||
|
||||
auto formats = std::vector<VkSurfaceFormatKHR>(count);
|
||||
vkc(
|
||||
vk_get_physical_device_surface_formats(m_physical_device, m_surface, &count, formats.data())
|
||||
);
|
||||
ensure(!formats.empty(), "Surface has no formats!");
|
||||
|
||||
// TODO(Light): parameterize
|
||||
constexpr auto desired_swapchain_image_count = uint32_t { 3 };
|
||||
const auto surface_format = formats.front();
|
||||
|
||||
const auto queue_indices = std::array<uint32_t, 2> {
|
||||
m_graphics_queue_family_index,
|
||||
m_present_queue_family_index,
|
||||
};
|
||||
|
||||
auto create_info = VkSwapchainCreateInfoKHR {
|
||||
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
||||
.surface = m_surface,
|
||||
.minImageCount = get_optimal_swapchain_image_count(
|
||||
capabilities,
|
||||
desired_swapchain_image_count
|
||||
),
|
||||
.imageFormat = surface_format.format,
|
||||
.imageColorSpace = surface_format.colorSpace,
|
||||
.imageExtent = m_framebuffer_size,
|
||||
.imageArrayLayers = 1u,
|
||||
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
||||
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = queue_indices.size(),
|
||||
.pQueueFamilyIndices = queue_indices.data(),
|
||||
.preTransform = capabilities.currentTransform,
|
||||
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
||||
.presentMode = VK_PRESENT_MODE_FIFO_RELAXED_KHR, // TODO(Light): parameterize
|
||||
.clipped = VK_TRUE,
|
||||
.oldSwapchain = nullptr,
|
||||
};
|
||||
|
||||
vkc(vk_create_swapchain_khr(m_device, &create_info, nullptr, &m_swapchain));
|
||||
vkc(vk_device_wait_idle(m_device));
|
||||
|
||||
auto image_count = uint32_t { 0u };
|
||||
vk_get_swapchain_images_khr(m_device, m_swapchain, &image_count, nullptr);
|
||||
|
||||
m_swapchain_images.resize(image_count);
|
||||
m_swapchain_image_views.resize(image_count);
|
||||
vk_get_swapchain_images_khr(m_device, m_swapchain, &image_count, m_swapchain_images.data());
|
||||
|
||||
for (auto [image, view] : std::views::zip(m_swapchain_images, m_swapchain_image_views))
|
||||
{
|
||||
auto create_info = VkImageViewCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = image,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = surface_format.format,
|
||||
.components = VkComponentMapping {
|
||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
},
|
||||
.subresourceRange = VkImageSubresourceRange {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0u,
|
||||
.levelCount = 1u,
|
||||
.baseArrayLayer = 0u,
|
||||
.layerCount = 1u,
|
||||
}
|
||||
};
|
||||
|
||||
vkc(vk_create_image_view(m_device, &create_info, nullptr, &view));
|
||||
}
|
||||
}
|
||||
|
||||
auto Context::get_optimal_swapchain_image_count(
|
||||
VkSurfaceCapabilitiesKHR capabilities,
|
||||
uint32_t desired_image_count
|
||||
) -> uint32_t
|
||||
{
|
||||
const auto min_image_count = capabilities.minImageCount;
|
||||
const auto max_image_count = capabilities.maxImageCount;
|
||||
|
||||
const auto has_max_limit = max_image_count != 0;
|
||||
|
||||
// Desired image count is in range
|
||||
if ((!has_max_limit || max_image_count >= desired_image_count)
|
||||
&& min_image_count <= desired_image_count)
|
||||
{
|
||||
return desired_image_count;
|
||||
}
|
||||
|
||||
// Fall-back to 2 if in ange
|
||||
if (min_image_count <= 2 && max_image_count >= 2)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Fall-back to min_image_count
|
||||
return min_image_count;
|
||||
}
|
||||
|
||||
void Context::load_library()
|
||||
{
|
||||
library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
|
||||
ensure(library, "Failed to dlopen libvulkan.so");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
vk_get_instance_proc_address = reinterpret_cast<PFN_vkGetInstanceProcAddr>(
|
||||
dlsym(library, "vkGetInstanceProcAddr")
|
||||
);
|
||||
ensure(vk_get_instance_proc_address, "Failed to load vulkan function: vkGetInstanceProcAddr");
|
||||
}
|
||||
|
||||
void Context::load_global_functions()
|
||||
{
|
||||
constexpr auto load_fn = []<typename T>(T &pfn, const char *fn_name) {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
pfn = reinterpret_cast<T>(vk_get_instance_proc_address(nullptr, fn_name));
|
||||
ensure(pfn, "Failed to load vulkan global function: {}", fn_name);
|
||||
// log_trc("Loaded global function: {}", fn_name);
|
||||
};
|
||||
|
||||
load_fn(vk_create_instance, "vkCreateInstance");
|
||||
load_fn(vk_enumerate_instance_extension_properties, "vkEnumerateInstanceExtensionProperties");
|
||||
load_fn(vk_enumerate_instance_layer_properties, "vkEnumerateInstanceLayerProperties");
|
||||
}
|
||||
|
||||
void Context::load_instance_functions()
|
||||
{
|
||||
const auto load_fn = [&]<typename T>(T &pfn, const char *fn_name) {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
pfn = reinterpret_cast<T>(vk_get_instance_proc_address(m_instance, fn_name));
|
||||
ensure(pfn, "Failed to load vulkan instance function: {}", fn_name);
|
||||
// log_trc("Loaded instance function: {}", fn_name);
|
||||
};
|
||||
|
||||
load_fn(vk_destroy_instance, "vkDestroyInstance");
|
||||
load_fn(vk_enumerate_physical_devices, "vkEnumeratePhysicalDevices");
|
||||
load_fn(vk_get_physical_device_properties, "vkGetPhysicalDeviceProperties");
|
||||
load_fn(
|
||||
vk_get_physical_device_queue_family_properties,
|
||||
"vkGetPhysicalDeviceQueueFamilyProperties"
|
||||
);
|
||||
load_fn(vk_create_device, "vkCreateDevice");
|
||||
load_fn(vk_get_device_proc_address, "vkGetDeviceProcAddr");
|
||||
load_fn(vk_destroy_device, "vkDestroyDevice");
|
||||
load_fn(vk_get_physical_device_features, "vkGetPhysicalDeviceFeatures");
|
||||
load_fn(vk_enumerate_device_extension_properties, "vkEnumerateDeviceExtensionProperties");
|
||||
|
||||
load_fn(vk_cmd_begin_debug_label, "vkCmdBeginDebugUtilsLabelEXT");
|
||||
load_fn(vk_cmd_end_debug_label, "vkCmdEndDebugUtilsLabelEXT");
|
||||
load_fn(vk_cmd_insert_debug_label, "vkCmdInsertDebugUtilsLabelEXT");
|
||||
load_fn(vk_create_debug_messenger, "vkCreateDebugUtilsMessengerEXT");
|
||||
load_fn(vk_destroy_debug_messenger, "vkDestroyDebugUtilsMessengerEXT");
|
||||
load_fn(vk_queue_begin_debug_label, "vkQueueBeginDebugUtilsLabelEXT");
|
||||
load_fn(vk_queue_end_debug_label, "vkQueueEndDebugUtilsLabelEXT");
|
||||
load_fn(vk_queue_insert_debug_label, "vkQueueInsertDebugUtilsLabelEXT");
|
||||
load_fn(vk_set_debug_object_name, "vkSetDebugUtilsObjectNameEXT");
|
||||
load_fn(vk_set_debug_object_tag, "vkSetDebugUtilsObjectTagEXT");
|
||||
load_fn(vk_submit_debug_message, "vkSubmitDebugUtilsMessageEXT");
|
||||
|
||||
load_fn(vk_get_physical_device_surface_support, "vkGetPhysicalDeviceSurfaceSupportKHR");
|
||||
load_fn(
|
||||
vk_get_physical_device_surface_capabilities,
|
||||
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR"
|
||||
);
|
||||
load_fn(vk_get_physical_device_surface_formats, "vkGetPhysicalDeviceSurfaceFormatsKHR");
|
||||
load_fn(vk_create_xlib_surface_khr, "vkCreateXlibSurfaceKHR");
|
||||
load_fn(vk_destroy_surface_khr, "vkDestroySurfaceKHR");
|
||||
}
|
||||
|
||||
void Context::load_device_functions()
|
||||
{
|
||||
const auto load_fn = [&]<typename T>(T &pfn, const char *fn_name) {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
pfn = reinterpret_cast<T>(vk_get_device_proc_address(m_device, fn_name));
|
||||
ensure(pfn, "Failed to load vulkan device function: {}", fn_name);
|
||||
// log_trc("Loaded device function: {}", fn_name);
|
||||
};
|
||||
|
||||
load_fn(vk_get_device_queue, "vkGetDeviceQueue");
|
||||
load_fn(vk_create_command_pool, "vkCreateCommandPool");
|
||||
load_fn(vk_destroy_command_pool, "vkDestroyCommandPool");
|
||||
load_fn(vk_allocate_command_buffers, "vkAllocateCommandBuffers");
|
||||
load_fn(vk_free_command_buffers, "vkFreeCommandBuffers");
|
||||
load_fn(vk_begin_command_buffer, "vkBeginCommandBuffer");
|
||||
load_fn(vk_end_command_buffer, "vkEndCommandBuffer");
|
||||
load_fn(vk_cmd_pipeline_barrier, "vkCmdPipelineBarrier");
|
||||
load_fn(vk_queue_submit, "vkQueueSubmit");
|
||||
load_fn(vk_queue_wait_idle, "vkQueueWaitIdle");
|
||||
load_fn(vk_device_wait_idle, "vkDeviceWaitIdle");
|
||||
load_fn(vk_create_fence, "vkCreateFence");
|
||||
load_fn(vk_destroy_fence, "vkDestroyFence");
|
||||
load_fn(vk_wait_for_fences, "vkWaitForFences");
|
||||
load_fn(vk_reset_fences, "vkResetFences");
|
||||
load_fn(vk_create_semaphore, "vkCreateSemaphore");
|
||||
load_fn(vk_destroy_semaphore, "vkDestroySemaphore");
|
||||
load_fn(vk_create_swapchain_khr, "vkCreateSwapchainKHR");
|
||||
load_fn(vk_destroy_swapchain_khr, "vkDestroySwapchainKHR");
|
||||
load_fn(vk_get_swapchain_images_khr, "vkGetSwapchainImagesKHR");
|
||||
load_fn(vk_acquire_next_image_khr, "vkAcquireNextImageKHR");
|
||||
load_fn(vk_queue_present_khr, "vkQueuePresentKHR");
|
||||
load_fn(vk_create_image_view, "vkCreateImageView");
|
||||
load_fn(vk_destroy_image_view, "vkDestroyImageView");
|
||||
load_fn(vk_create_render_pass, "vkCreateRenderPass");
|
||||
load_fn(vk_destroy_render_pass, "vkDestroyRenderPass");
|
||||
load_fn(vk_create_frame_buffer, "vkCreateFramebuffer");
|
||||
load_fn(vk_destroy_frame_buffer, "vkDestroyFramebuffer");
|
||||
load_fn(vk_create_shader_module, "vkCreateShaderModule");
|
||||
load_fn(vk_destroy_shader_module, "vkDestroyShaderModule");
|
||||
load_fn(vk_create_pipeline_layout, "vkCreatePipelineLayout");
|
||||
load_fn(vk_destroy_pipeline_layout, "vkDestroyPipelineLayout");
|
||||
load_fn(vk_create_graphics_pipelines, "vkCreateGraphicsPipelines");
|
||||
load_fn(vk_destroy_pipeline, "vkDestroyPipeline");
|
||||
load_fn(vk_cmd_begin_render_pass, "vkCmdBeginRenderPass");
|
||||
load_fn(vk_cmd_end_render_pass, "vkCmdEndRenderPass");
|
||||
load_fn(vk_cmd_bind_pipeline, "vkCmdBindPipeline");
|
||||
load_fn(vk_cmd_draw, "vkCmdDraw");
|
||||
load_fn(vk_cmd_set_viewport, "vkCmdSetViewport");
|
||||
load_fn(vk_cmd_set_scissors, "vkCmdSetScissor");
|
||||
}
|
||||
|
||||
[[nodiscard]] auto Context::find_suitable_queue_family() const -> uint32_t
|
||||
{
|
||||
auto count = 0u;
|
||||
vk_get_physical_device_queue_family_properties(m_physical_device, &count, nullptr);
|
||||
ensure(count != 0u, "Failed to find any physical devices with Vulkan support");
|
||||
|
||||
auto families = std::vector<VkQueueFamilyProperties>(count);
|
||||
vk_get_physical_device_queue_family_properties(m_physical_device, &count, families.data());
|
||||
|
||||
const auto required_flags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT;
|
||||
for (auto idx = 0u; auto &family : families)
|
||||
{
|
||||
if ((family.queueFlags & required_flags) == required_flags)
|
||||
{
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
|
||||
ensure(false, "Failed to find a suitable Vulkan queue family");
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto parse_message_type(VkDebugUtilsMessageTypeFlagsEXT message_types) -> const char *
|
||||
{
|
||||
if (message_types == VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT)
|
||||
{
|
||||
return "GENERAL";
|
||||
}
|
||||
|
||||
if (message_types
|
||||
== (VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
|
||||
| VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT))
|
||||
{
|
||||
return "VALIDATION | PERFORMANCE";
|
||||
}
|
||||
|
||||
if (message_types == VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)
|
||||
{
|
||||
return "VALIDATION";
|
||||
}
|
||||
|
||||
return "PERFORMANCE";
|
||||
}
|
||||
|
||||
auto parse_message_severity(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity)
|
||||
-> app::SystemDiagnosis::Severity
|
||||
{
|
||||
using enum app::SystemDiagnosis::Severity;
|
||||
|
||||
switch (message_severity)
|
||||
{
|
||||
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: return verbose;
|
||||
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: return info;
|
||||
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: return warning;
|
||||
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: return error;
|
||||
default: ensure(false, "Invalid message severity: {}", static_cast<int>(message_severity));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto validation_layers_callback(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT const message_severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT const message_types,
|
||||
VkDebugUtilsMessengerCallbackDataEXT const *const callback_data,
|
||||
void *const vulkan_user_data
|
||||
) -> VkBool32
|
||||
{
|
||||
log_dbg("VALIDATION: {}", callback_data->pMessage);
|
||||
ensure(vulkan_user_data, "Validation layers's user data is not set!");
|
||||
|
||||
auto stats = *(Ref<app::SystemStats> *)vulkan_user_data; // NOLINT
|
||||
|
||||
const auto &type = parse_message_type(message_types);
|
||||
|
||||
auto message = std::format(
|
||||
"Vulkan Validation Message:\ntype: {}\nseverity: {}\nmessage: {}",
|
||||
type,
|
||||
std::to_underlying(parse_message_severity(message_severity)),
|
||||
callback_data->pMessage
|
||||
);
|
||||
|
||||
auto severity = parse_message_severity(message_severity);
|
||||
if (std::to_underlying(severity) < 2)
|
||||
{
|
||||
return static_cast<VkBool32>(VK_FALSE);
|
||||
}
|
||||
|
||||
stats->push_diagnosis(
|
||||
app::SystemDiagnosis {
|
||||
.message = message,
|
||||
.code = {}, // TODO(Light): extract vulkan validation-layers code from the message
|
||||
.severity = severity,
|
||||
}
|
||||
);
|
||||
return static_cast<VkBool32>(VK_FALSE);
|
||||
}
|
||||
|
||||
|
||||
} // namespace lt::renderer::vk
|
||||
14
modules/renderer/private/vk/context/context.cpp
Normal file
14
modules/renderer/private/vk/context/context.cpp
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#include <renderer/vk/context/context.hpp>
|
||||
#include <renderer/vk/context/instance.hpp>
|
||||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
Context::Context(const ecs::Entity &surface_entity, Ref<app::SystemStats> system_stats)
|
||||
: m_stats(std::move(system_stats))
|
||||
, m_surface(surface_entity)
|
||||
, m_device(m_surface)
|
||||
, m_swapchain(m_device, m_surface)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace lt::renderer::vk
|
||||
48
modules/renderer/private/vk/context/context.hpp
Normal file
48
modules/renderer/private/vk/context/context.hpp
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
|
||||
#include <app/system.hpp>
|
||||
#include <ecs/entity.hpp>
|
||||
#include <memory/pointer_types/null_on_move.hpp>
|
||||
#include <surface/components.hpp>
|
||||
|
||||
//
|
||||
#include <renderer/vk/context/device.hpp>
|
||||
#include <renderer/vk/context/instance.hpp>
|
||||
#include <renderer/vk/context/surface.hpp>
|
||||
#include <renderer/vk/context/swapchain.hpp>
|
||||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
using memory::NullOnMove;
|
||||
|
||||
class Context
|
||||
{
|
||||
public:
|
||||
Context(const ecs::Entity &surface_entity, Ref<app::SystemStats> system_stats);
|
||||
|
||||
[[nodiscard]] auto instance() const -> VkInstance
|
||||
{
|
||||
return Instance::get();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto device() const -> const Device &
|
||||
{
|
||||
return m_device;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto swapchain() const -> const Swapchain &
|
||||
{
|
||||
return m_swapchain;
|
||||
}
|
||||
|
||||
private:
|
||||
Ref<app::SystemStats> m_stats;
|
||||
|
||||
Surface m_surface;
|
||||
|
||||
Device m_device;
|
||||
|
||||
Swapchain m_swapchain;
|
||||
};
|
||||
|
||||
} // namespace lt::renderer::vk
|
||||
156
modules/renderer/private/vk/context/device.cpp
Normal file
156
modules/renderer/private/vk/context/device.cpp
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
#include <renderer/vk/context/device.hpp>
|
||||
#include <renderer/vk/context/instance.hpp>
|
||||
#include <renderer/vk/context/surface.hpp>
|
||||
#include <renderer/vk/debug/validation.hpp>
|
||||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
Device::Device(const Surface &surface)
|
||||
{
|
||||
ensure(surface.vk(), "Failed to initialize vk::Device: null vulkan surface");
|
||||
|
||||
initialize_physical_device();
|
||||
initialize_logical_device();
|
||||
Instance::load_device_functions(m_device);
|
||||
initialize_queue(surface);
|
||||
}
|
||||
|
||||
Device::~Device()
|
||||
{
|
||||
if (m_device)
|
||||
{
|
||||
vkc(vk_device_wait_idle(m_device));
|
||||
vk_destroy_device(m_device, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void Device::initialize_physical_device()
|
||||
{
|
||||
auto count = 0u;
|
||||
vkc(vk_enumerate_physical_devices(Instance::get(), &count, nullptr));
|
||||
ensure(count != 0u, "Failed to find any physical devices with Vulkan support");
|
||||
|
||||
auto devices = std::vector<VkPhysicalDevice>(count);
|
||||
vkc(vk_enumerate_physical_devices(Instance::get(), &count, devices.data()));
|
||||
|
||||
for (auto &device : devices)
|
||||
{
|
||||
auto properties = VkPhysicalDeviceProperties {};
|
||||
auto features = VkPhysicalDeviceFeatures {};
|
||||
|
||||
vk_get_physical_device_properties(device, &properties);
|
||||
vk_get_physical_device_features(device, &features);
|
||||
|
||||
if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
|
||||
&& features.geometryShader)
|
||||
{
|
||||
m_physical_device = device;
|
||||
}
|
||||
}
|
||||
ensure(m_physical_device, "Failed to find any suitable Vulkan physical device");
|
||||
}
|
||||
|
||||
void Device::initialize_logical_device()
|
||||
{
|
||||
const float priorities = .0f;
|
||||
|
||||
auto queue_info = VkDeviceQueueCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||
.queueFamilyIndex = find_suitable_queue_family(),
|
||||
.queueCount = 1u,
|
||||
.pQueuePriorities = &priorities,
|
||||
};
|
||||
|
||||
auto physical_device_features = VkPhysicalDeviceFeatures {};
|
||||
|
||||
auto extensions = std::vector<const char *> {
|
||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||
};
|
||||
|
||||
auto device_info = VkDeviceCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||
.queueCreateInfoCount = 1,
|
||||
.pQueueCreateInfos = &queue_info,
|
||||
.enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
|
||||
.ppEnabledExtensionNames = extensions.data(),
|
||||
.pEnabledFeatures = &physical_device_features,
|
||||
};
|
||||
|
||||
ensure(
|
||||
!vk_create_device(m_physical_device, &device_info, nullptr, &m_device),
|
||||
"Failed to create logical vulkan device"
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto Device::find_suitable_queue_family() const -> uint32_t
|
||||
{
|
||||
auto count = 0u;
|
||||
vk_get_physical_device_queue_family_properties(m_physical_device, &count, nullptr);
|
||||
ensure(count != 0u, "Failed to find any physical devices with Vulkan support");
|
||||
|
||||
auto families = std::vector<VkQueueFamilyProperties>(count);
|
||||
vk_get_physical_device_queue_family_properties(m_physical_device, &count, families.data());
|
||||
|
||||
const auto required_flags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT;
|
||||
for (auto idx = 0u; auto &family : families)
|
||||
{
|
||||
if ((family.queueFlags & required_flags) == required_flags)
|
||||
{
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
|
||||
ensure(false, "Failed to find a suitable Vulkan queue family");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Device::initialize_queue(const Surface &surface)
|
||||
{
|
||||
vk_get_device_queue(m_device, find_suitable_queue_family(), 0, &m_queue);
|
||||
|
||||
auto count = uint32_t { 0u };
|
||||
vk_get_physical_device_queue_family_properties(m_physical_device, &count, nullptr);
|
||||
|
||||
auto properties = std::vector<VkQueueFamilyProperties>(count);
|
||||
vk_get_physical_device_queue_family_properties(m_physical_device, &count, properties.data());
|
||||
|
||||
for (auto idx = uint32_t { 0u }; const auto &property : properties)
|
||||
{
|
||||
if (property.queueFlags & VK_QUEUE_GRAPHICS_BIT)
|
||||
{
|
||||
m_graphics_queue_family_index = idx;
|
||||
}
|
||||
|
||||
auto has_presentation_support = VkBool32 { false };
|
||||
vkc(vk_get_physical_device_surface_support(
|
||||
m_physical_device,
|
||||
idx,
|
||||
surface.vk(),
|
||||
&has_presentation_support
|
||||
));
|
||||
if (has_presentation_support)
|
||||
{
|
||||
m_present_queue_family_index = idx;
|
||||
}
|
||||
|
||||
++idx;
|
||||
|
||||
if (m_graphics_queue_family_index != VK_QUEUE_FAMILY_IGNORED
|
||||
&& m_present_queue_family_index != VK_QUEUE_FAMILY_IGNORED)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ensure(
|
||||
m_graphics_queue_family_index != VK_QUEUE_FAMILY_IGNORED,
|
||||
"Failed to find graphics queue family"
|
||||
);
|
||||
|
||||
ensure(
|
||||
m_present_queue_family_index != VK_QUEUE_FAMILY_IGNORED,
|
||||
"Failed to find presentation queue family"
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace lt::renderer::vk
|
||||
59
modules/renderer/private/vk/context/device.hpp
Normal file
59
modules/renderer/private/vk/context/device.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory/pointer_types/null_on_move.hpp>
|
||||
#include <renderer/vk/vulkan.hpp>
|
||||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
class Device
|
||||
{
|
||||
public:
|
||||
Device(const class Surface &surface);
|
||||
|
||||
~Device();
|
||||
|
||||
Device(Device &&) = default;
|
||||
|
||||
Device(const Device &) = delete;
|
||||
|
||||
auto operator=(Device &&) -> Device & = default;
|
||||
|
||||
auto operator=(const Device &) const -> Device & = delete;
|
||||
|
||||
[[nodiscard]] auto vk() const -> VkDevice
|
||||
{
|
||||
return m_device;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto physical() const -> VkPhysicalDevice
|
||||
{
|
||||
return m_physical_device;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_family_indices() const -> std::array<uint32_t, 2>
|
||||
{
|
||||
return { m_graphics_queue_family_index, m_present_queue_family_index };
|
||||
}
|
||||
|
||||
private:
|
||||
void initialize_physical_device();
|
||||
|
||||
void initialize_logical_device();
|
||||
|
||||
void initialize_queue(const class Surface &surface);
|
||||
|
||||
[[nodiscard]] auto find_suitable_queue_family() const -> uint32_t;
|
||||
|
||||
// logical device
|
||||
memory::NullOnMove<VkPhysicalDevice> m_physical_device = VK_NULL_HANDLE;
|
||||
|
||||
memory::NullOnMove<VkDevice> m_device = VK_NULL_HANDLE;
|
||||
|
||||
memory::NullOnMove<VkQueue> m_queue = VK_NULL_HANDLE;
|
||||
|
||||
uint32_t m_graphics_queue_family_index = VK_QUEUE_FAMILY_IGNORED;
|
||||
|
||||
uint32_t m_present_queue_family_index = VK_QUEUE_FAMILY_IGNORED;
|
||||
};
|
||||
|
||||
} // namespace lt::renderer::vk
|
||||
99
modules/renderer/private/vk/context/device.test.cpp
Normal file
99
modules/renderer/private/vk/context/device.test.cpp
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
#include <ranges>
|
||||
#include <renderer/vk/context/device.hpp>
|
||||
#include <renderer/vk/context/surface.hpp>
|
||||
#include <surface/components.hpp>
|
||||
#include <surface/system.hpp>
|
||||
#include <test/test.hpp>
|
||||
|
||||
using namespace lt;
|
||||
using renderer::vk::Device;
|
||||
using renderer::vk::Surface;
|
||||
using test::Case;
|
||||
using test::expect_ne;
|
||||
using test::expect_throw;
|
||||
using test::Suite;
|
||||
|
||||
constexpr auto resolution = math::uvec2 { 800u, 600u };
|
||||
|
||||
Suite raii = "device_raii"_suite = [] {
|
||||
Case { "happy path won't throw" } = [] {
|
||||
auto registry = create_ref<ecs::Registry>();
|
||||
auto surface_system = surface::System { registry };
|
||||
auto entity = ecs::Entity { registry, registry->create_entity() };
|
||||
entity.add<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {
|
||||
.resolution = resolution,
|
||||
.visible = true,
|
||||
});
|
||||
|
||||
auto surface = Surface { entity };
|
||||
auto device = Device { surface };
|
||||
};
|
||||
|
||||
Case { "many won't freeze/throw" } = [] {
|
||||
auto registry = create_ref<ecs::Registry>();
|
||||
auto surface_system = surface::System { registry };
|
||||
auto entity = ecs::Entity { registry, registry->create_entity() };
|
||||
entity.add<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {
|
||||
.resolution = resolution,
|
||||
.visible = true,
|
||||
});
|
||||
auto surface = Surface { entity };
|
||||
|
||||
// it takes a loong time to initialize vulkan + setup device
|
||||
for (auto idx : std::views::iota(0, 10))
|
||||
{
|
||||
Device { surface };
|
||||
}
|
||||
};
|
||||
|
||||
Case { "unhappy path throws" } = [] {
|
||||
auto registry = create_ref<ecs::Registry>();
|
||||
auto surface_system = surface::System { registry };
|
||||
auto entity = ecs::Entity { registry, registry->create_entity() };
|
||||
entity.add<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {
|
||||
.resolution = resolution,
|
||||
.visible = true,
|
||||
});
|
||||
|
||||
auto moved_out_surface = Surface { entity };
|
||||
auto surface = std::move(moved_out_surface);
|
||||
|
||||
expect_throw([&] { Device { moved_out_surface }; });
|
||||
};
|
||||
|
||||
Case { "post construct has correct state" } = [] {
|
||||
auto registry = create_ref<ecs::Registry>();
|
||||
auto surface_system = surface::System { registry };
|
||||
auto entity = ecs::Entity { registry, registry->create_entity() };
|
||||
entity.add<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {
|
||||
.resolution = resolution,
|
||||
.visible = true,
|
||||
});
|
||||
|
||||
auto surface = Surface { entity };
|
||||
auto device = Device { surface };
|
||||
|
||||
for (auto &index : device.get_family_indices())
|
||||
{
|
||||
expect_ne(index, VK_QUEUE_FAMILY_IGNORED);
|
||||
}
|
||||
test::expect_true(device.physical());
|
||||
test::expect_true(device.vk());
|
||||
};
|
||||
|
||||
Case { "post destruct has correct state" } = [] {
|
||||
auto registry = create_ref<ecs::Registry>();
|
||||
auto surface_system = surface::System { registry };
|
||||
auto entity = ecs::Entity { registry, registry->create_entity() };
|
||||
entity.add<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {
|
||||
.resolution = resolution,
|
||||
.visible = true,
|
||||
});
|
||||
|
||||
auto surface = Surface { entity };
|
||||
|
||||
{
|
||||
auto device = Device { surface };
|
||||
}
|
||||
};
|
||||
};
|
||||
455
modules/renderer/private/vk/context/instance.cpp
Normal file
455
modules/renderer/private/vk/context/instance.cpp
Normal file
|
|
@ -0,0 +1,455 @@
|
|||
#include <app/system.hpp>
|
||||
#include <renderer/vk/context/instance.hpp>
|
||||
#include <renderer/vk/debug/validation.hpp>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#error "Unsupported platform (currently)"
|
||||
#elif defined(__unix__)
|
||||
#include <dlfcn.h>
|
||||
namespace {
|
||||
void *library = nullptr; // NOLINT
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
// global functions
|
||||
PFN_vkGetInstanceProcAddr vk_get_instance_proc_address {};
|
||||
PFN_vkCreateInstance vk_create_instance {};
|
||||
PFN_vkEnumerateInstanceExtensionProperties vk_enumerate_instance_extension_properties {};
|
||||
PFN_vkEnumerateInstanceLayerProperties vk_enumerate_instance_layer_properties {};
|
||||
|
||||
// instance functions
|
||||
PFN_vkDestroyInstance vk_destroy_instance {};
|
||||
PFN_vkEnumeratePhysicalDevices vk_enumerate_physical_devices {};
|
||||
PFN_vkGetPhysicalDeviceProperties vk_get_physical_device_properties {};
|
||||
PFN_vkGetPhysicalDeviceQueueFamilyProperties vk_get_physical_device_queue_family_properties {};
|
||||
PFN_vkCreateDevice vk_create_device {};
|
||||
PFN_vkGetDeviceProcAddr vk_get_device_proc_address {};
|
||||
PFN_vkDestroyDevice vk_destroy_device {};
|
||||
PFN_vkGetPhysicalDeviceFeatures vk_get_physical_device_features {};
|
||||
PFN_vkEnumerateDeviceExtensionProperties vk_enumerate_device_extension_properties {};
|
||||
|
||||
// extension instance functions
|
||||
PFN_vkCmdBeginDebugUtilsLabelEXT vk_cmd_begin_debug_label {};
|
||||
PFN_vkCmdEndDebugUtilsLabelEXT vk_cmd_end_debug_label {};
|
||||
PFN_vkCmdInsertDebugUtilsLabelEXT vk_cmd_insert_debug_label {};
|
||||
PFN_vkCreateDebugUtilsMessengerEXT vk_create_debug_messenger {};
|
||||
PFN_vkDestroyDebugUtilsMessengerEXT vk_destroy_debug_messenger {};
|
||||
PFN_vkQueueBeginDebugUtilsLabelEXT vk_queue_begin_debug_label {};
|
||||
PFN_vkQueueEndDebugUtilsLabelEXT vk_queue_end_debug_label {};
|
||||
PFN_vkQueueInsertDebugUtilsLabelEXT vk_queue_insert_debug_label {};
|
||||
PFN_vkSetDebugUtilsObjectNameEXT vk_set_debug_object_name {};
|
||||
PFN_vkSetDebugUtilsObjectTagEXT vk_set_debug_object_tag {};
|
||||
PFN_vkSubmitDebugUtilsMessageEXT vk_submit_debug_message {};
|
||||
|
||||
// device functions
|
||||
PFN_vkGetDeviceQueue vk_get_device_queue {};
|
||||
PFN_vkCreateCommandPool vk_create_command_pool {};
|
||||
PFN_vkDestroyCommandPool vk_destroy_command_pool {};
|
||||
PFN_vkAllocateCommandBuffers vk_allocate_command_buffers {};
|
||||
PFN_vkFreeCommandBuffers vk_free_command_buffers {};
|
||||
PFN_vkBeginCommandBuffer vk_begin_command_buffer {};
|
||||
PFN_vkEndCommandBuffer vk_end_command_buffer {};
|
||||
PFN_vkCmdPipelineBarrier vk_cmd_pipeline_barrier {};
|
||||
PFN_vkQueueSubmit vk_queue_submit {};
|
||||
PFN_vkQueueWaitIdle vk_queue_wait_idle {};
|
||||
PFN_vkDeviceWaitIdle vk_device_wait_idle {};
|
||||
PFN_vkCreateFence vk_create_fence {};
|
||||
PFN_vkDestroyFence vk_destroy_fence {};
|
||||
PFN_vkWaitForFences vk_wait_for_fences {};
|
||||
PFN_vkResetFences vk_reset_fences {};
|
||||
PFN_vkCreateSemaphore vk_create_semaphore {};
|
||||
PFN_vkDestroySemaphore vk_destroy_semaphore {};
|
||||
PFN_vkCreateSwapchainKHR vk_create_swapchain_khr {};
|
||||
PFN_vkDestroySwapchainKHR vk_destroy_swapchain_khr {};
|
||||
PFN_vkGetSwapchainImagesKHR vk_get_swapchain_images_khr {};
|
||||
PFN_vkAcquireNextImageKHR vk_acquire_next_image_khr {};
|
||||
PFN_vkQueuePresentKHR vk_queue_present_khr {};
|
||||
PFN_vkCreateImageView vk_create_image_view {};
|
||||
PFN_vkDestroyImageView vk_destroy_image_view {};
|
||||
PFN_vkCreateRenderPass vk_create_render_pass {};
|
||||
PFN_vkDestroyRenderPass vk_destroy_render_pass {};
|
||||
PFN_vkCreateFramebuffer vk_create_frame_buffer {};
|
||||
PFN_vkDestroyFramebuffer vk_destroy_frame_buffer {};
|
||||
PFN_vkCreateShaderModule vk_create_shader_module {};
|
||||
PFN_vkDestroyShaderModule vk_destroy_shader_module {};
|
||||
PFN_vkCreatePipelineLayout vk_create_pipeline_layout {};
|
||||
PFN_vkDestroyPipelineLayout vk_destroy_pipeline_layout {};
|
||||
PFN_vkCreateGraphicsPipelines vk_create_graphics_pipelines {};
|
||||
PFN_vkDestroyPipeline vk_destroy_pipeline {};
|
||||
PFN_vkCmdBeginRenderPass vk_cmd_begin_render_pass {};
|
||||
PFN_vkCmdEndRenderPass vk_cmd_end_render_pass {};
|
||||
PFN_vkCmdBindPipeline vk_cmd_bind_pipeline {};
|
||||
PFN_vkCmdDraw vk_cmd_draw {};
|
||||
PFN_vkCmdSetViewport vk_cmd_set_viewport {};
|
||||
PFN_vkCmdSetScissor vk_cmd_set_scissors {};
|
||||
|
||||
PFN_vkGetPhysicalDeviceSurfaceSupportKHR vk_get_physical_device_surface_support {};
|
||||
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vk_get_physical_device_surface_capabilities {};
|
||||
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vk_get_physical_device_surface_formats {};
|
||||
|
||||
PFN_vkCreateXlibSurfaceKHR vk_create_xlib_surface_khr {};
|
||||
PFN_vkDestroySurfaceKHR vk_destroy_surface_khr {};
|
||||
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
auto parse_message_type(VkDebugUtilsMessageTypeFlagsEXT message_types) -> const char *;
|
||||
|
||||
auto parse_message_severity(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity)
|
||||
-> app::SystemDiagnosis::Severity;
|
||||
|
||||
auto validation_layers_callback(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT message_types,
|
||||
VkDebugUtilsMessengerCallbackDataEXT const *callback_data,
|
||||
void *vulkan_user_data
|
||||
) -> VkBool32;
|
||||
|
||||
|
||||
Instance::Instance()
|
||||
{
|
||||
load_library();
|
||||
load_global_functions();
|
||||
|
||||
initialize_instance();
|
||||
load_instance_functions();
|
||||
}
|
||||
|
||||
Instance::~Instance()
|
||||
{
|
||||
vk_destroy_instance(m_instance, nullptr);
|
||||
unload_library();
|
||||
}
|
||||
|
||||
void Instance::initialize_instance()
|
||||
{
|
||||
auto app_info = VkApplicationInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||
.pApplicationName = "Hallo Hallo Hallo :3",
|
||||
.applicationVersion = VK_MAKE_VERSION(1, 4, 0),
|
||||
.pEngineName = "light",
|
||||
.engineVersion = VK_MAKE_VERSION(1, 4, 0),
|
||||
.apiVersion = VK_API_VERSION_1_4,
|
||||
};
|
||||
|
||||
auto extensions = std::vector<const char *> {
|
||||
VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
|
||||
VK_KHR_SURFACE_EXTENSION_NAME,
|
||||
VK_KHR_XLIB_SURFACE_EXTENSION_NAME,
|
||||
};
|
||||
|
||||
const char *layer_name = "VK_LAYER_KHRONOS_validation";
|
||||
const auto setting_validate_core = VkBool32 { VK_TRUE };
|
||||
const auto setting_validate_sync = VkBool32 { VK_TRUE };
|
||||
const auto setting_thread_safety = VkBool32 { VK_TRUE };
|
||||
const auto *setting_debug_action = "";
|
||||
const auto setting_enable_message_limit = VkBool32 { VK_TRUE };
|
||||
const auto setting_duplicate_message_limit = uint32_t { 3u };
|
||||
auto setting_report_flags = std::array<const char *, 5> {
|
||||
"info", "warn", "perf", "error", "verbose",
|
||||
};
|
||||
|
||||
const auto settings = std::array<VkLayerSettingEXT, 7>({
|
||||
{
|
||||
.pLayerName = layer_name,
|
||||
.pSettingName = "validate_core",
|
||||
.type = VK_LAYER_SETTING_TYPE_BOOL32_EXT,
|
||||
.valueCount = 1,
|
||||
.pValues = &setting_validate_core,
|
||||
},
|
||||
{
|
||||
.pLayerName = layer_name,
|
||||
.pSettingName = "validate_sync",
|
||||
.type = VK_LAYER_SETTING_TYPE_BOOL32_EXT,
|
||||
.valueCount = 1,
|
||||
.pValues = &setting_validate_sync,
|
||||
},
|
||||
{
|
||||
.pLayerName = layer_name,
|
||||
.pSettingName = "thread_safety",
|
||||
.type = VK_LAYER_SETTING_TYPE_BOOL32_EXT,
|
||||
.valueCount = 1,
|
||||
.pValues = &setting_thread_safety,
|
||||
},
|
||||
{
|
||||
.pLayerName = layer_name,
|
||||
.pSettingName = "debug_action",
|
||||
.type = VK_LAYER_SETTING_TYPE_STRING_EXT,
|
||||
.valueCount = 1,
|
||||
.pValues = static_cast<const void *>(&setting_debug_action),
|
||||
},
|
||||
{
|
||||
.pLayerName = layer_name,
|
||||
.pSettingName = "report_flags",
|
||||
.type = VK_LAYER_SETTING_TYPE_STRING_EXT,
|
||||
.valueCount = setting_report_flags.size(),
|
||||
.pValues = static_cast<const void *>(setting_report_flags.data()),
|
||||
},
|
||||
{
|
||||
.pLayerName = layer_name,
|
||||
.pSettingName = "enable_message_limit",
|
||||
.type = VK_LAYER_SETTING_TYPE_BOOL32_EXT,
|
||||
.valueCount = 1,
|
||||
.pValues = &setting_enable_message_limit,
|
||||
},
|
||||
{
|
||||
.pLayerName = layer_name,
|
||||
.pSettingName = "duplicate_message_limit",
|
||||
.type = VK_LAYER_SETTING_TYPE_UINT32_EXT,
|
||||
.valueCount = 1u,
|
||||
.pValues = &setting_duplicate_message_limit,
|
||||
},
|
||||
});
|
||||
|
||||
const VkLayerSettingsCreateInfoEXT layer_settings_create_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT,
|
||||
.settingCount = settings.size(),
|
||||
.pSettings = settings.data()
|
||||
};
|
||||
|
||||
auto layers = std::vector<const char *> {
|
||||
"VK_LAYER_KHRONOS_validation",
|
||||
};
|
||||
|
||||
auto instance_info = VkInstanceCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||||
.pNext = &layer_settings_create_info,
|
||||
.pApplicationInfo = &app_info,
|
||||
.enabledLayerCount = static_cast<uint32_t>(layers.size()),
|
||||
.ppEnabledLayerNames = layers.data(),
|
||||
.enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
|
||||
.ppEnabledExtensionNames = extensions.data(),
|
||||
};
|
||||
|
||||
{
|
||||
auto count = 0u;
|
||||
vkc(vk_enumerate_instance_extension_properties(nullptr, &count, nullptr));
|
||||
|
||||
auto extensions = std::vector<VkExtensionProperties>(count);
|
||||
vkc(vk_enumerate_instance_extension_properties(nullptr, &count, extensions.data()));
|
||||
|
||||
// log_inf("Available vulkan instance extensions:");
|
||||
for (auto &ext : extensions)
|
||||
{
|
||||
// log_inf("\t{} @ {}", ext.extensionName, ext.specVersion);
|
||||
}
|
||||
}
|
||||
|
||||
vkc(vk_create_instance(&instance_info, nullptr, &m_instance));
|
||||
ensure(m_instance, "Failed to create vulkan instance");
|
||||
}
|
||||
|
||||
void Instance::load_library()
|
||||
{
|
||||
library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
|
||||
ensure(library, "Failed to dlopen libvulkan.so");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
vk_get_instance_proc_address = reinterpret_cast<PFN_vkGetInstanceProcAddr>(
|
||||
dlsym(library, "vkGetInstanceProcAddr")
|
||||
);
|
||||
ensure(vk_get_instance_proc_address, "Failed to load vulkan function: vkGetInstanceProcAddr");
|
||||
}
|
||||
|
||||
void Instance::unload_library()
|
||||
{
|
||||
if (!library)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// dlclose(library);
|
||||
// library = nullptr;
|
||||
}
|
||||
|
||||
void Instance::load_global_functions()
|
||||
{
|
||||
constexpr auto load_fn = []<typename T>(T &pfn, const char *fn_name) {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
pfn = reinterpret_cast<T>(vk_get_instance_proc_address(nullptr, fn_name));
|
||||
ensure(pfn, "Failed to load vulkan global function: {}", fn_name);
|
||||
// log_trc("Loaded global function: {}", fn_name);
|
||||
};
|
||||
|
||||
load_fn(vk_create_instance, "vkCreateInstance");
|
||||
load_fn(vk_enumerate_instance_extension_properties, "vkEnumerateInstanceExtensionProperties");
|
||||
load_fn(vk_enumerate_instance_layer_properties, "vkEnumerateInstanceLayerProperties");
|
||||
}
|
||||
|
||||
void Instance::load_instance_functions()
|
||||
{
|
||||
const auto load_fn = [&]<typename T>(T &pfn, const char *fn_name) {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
pfn = reinterpret_cast<T>(vk_get_instance_proc_address(m_instance, fn_name));
|
||||
ensure(pfn, "Failed to load vulkan instance function: {}", fn_name);
|
||||
// log_trc("Loaded instance function: {}", fn_name);
|
||||
};
|
||||
|
||||
load_fn(vk_destroy_instance, "vkDestroyInstance");
|
||||
load_fn(vk_enumerate_physical_devices, "vkEnumeratePhysicalDevices");
|
||||
load_fn(vk_get_physical_device_properties, "vkGetPhysicalDeviceProperties");
|
||||
load_fn(
|
||||
vk_get_physical_device_queue_family_properties,
|
||||
"vkGetPhysicalDeviceQueueFamilyProperties"
|
||||
);
|
||||
load_fn(vk_create_device, "vkCreateDevice");
|
||||
load_fn(vk_get_device_proc_address, "vkGetDeviceProcAddr");
|
||||
load_fn(vk_destroy_device, "vkDestroyDevice");
|
||||
load_fn(vk_get_physical_device_features, "vkGetPhysicalDeviceFeatures");
|
||||
load_fn(vk_enumerate_device_extension_properties, "vkEnumerateDeviceExtensionProperties");
|
||||
|
||||
load_fn(vk_cmd_begin_debug_label, "vkCmdBeginDebugUtilsLabelEXT");
|
||||
load_fn(vk_cmd_end_debug_label, "vkCmdEndDebugUtilsLabelEXT");
|
||||
load_fn(vk_cmd_insert_debug_label, "vkCmdInsertDebugUtilsLabelEXT");
|
||||
load_fn(vk_create_debug_messenger, "vkCreateDebugUtilsMessengerEXT");
|
||||
load_fn(vk_destroy_debug_messenger, "vkDestroyDebugUtilsMessengerEXT");
|
||||
load_fn(vk_queue_begin_debug_label, "vkQueueBeginDebugUtilsLabelEXT");
|
||||
load_fn(vk_queue_end_debug_label, "vkQueueEndDebugUtilsLabelEXT");
|
||||
load_fn(vk_queue_insert_debug_label, "vkQueueInsertDebugUtilsLabelEXT");
|
||||
load_fn(vk_set_debug_object_name, "vkSetDebugUtilsObjectNameEXT");
|
||||
load_fn(vk_set_debug_object_tag, "vkSetDebugUtilsObjectTagEXT");
|
||||
load_fn(vk_submit_debug_message, "vkSubmitDebugUtilsMessageEXT");
|
||||
|
||||
load_fn(vk_get_physical_device_surface_support, "vkGetPhysicalDeviceSurfaceSupportKHR");
|
||||
load_fn(
|
||||
vk_get_physical_device_surface_capabilities,
|
||||
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR"
|
||||
);
|
||||
load_fn(vk_get_physical_device_surface_formats, "vkGetPhysicalDeviceSurfaceFormatsKHR");
|
||||
load_fn(vk_create_xlib_surface_khr, "vkCreateXlibSurfaceKHR");
|
||||
load_fn(vk_destroy_surface_khr, "vkDestroySurfaceKHR");
|
||||
}
|
||||
|
||||
void Instance::load_device_functions_impl(VkDevice device)
|
||||
{
|
||||
const auto load_fn = [&]<typename T>(T &pfn, const char *fn_name) {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
pfn = reinterpret_cast<T>(vk_get_device_proc_address(device, fn_name));
|
||||
ensure(pfn, "Failed to load vulkan device function: {}", fn_name);
|
||||
// log_trc("Loaded device function: {}", fn_name);
|
||||
};
|
||||
|
||||
load_fn(vk_get_device_queue, "vkGetDeviceQueue");
|
||||
load_fn(vk_create_command_pool, "vkCreateCommandPool");
|
||||
load_fn(vk_destroy_command_pool, "vkDestroyCommandPool");
|
||||
load_fn(vk_allocate_command_buffers, "vkAllocateCommandBuffers");
|
||||
load_fn(vk_free_command_buffers, "vkFreeCommandBuffers");
|
||||
load_fn(vk_begin_command_buffer, "vkBeginCommandBuffer");
|
||||
load_fn(vk_end_command_buffer, "vkEndCommandBuffer");
|
||||
load_fn(vk_cmd_pipeline_barrier, "vkCmdPipelineBarrier");
|
||||
load_fn(vk_queue_submit, "vkQueueSubmit");
|
||||
load_fn(vk_queue_wait_idle, "vkQueueWaitIdle");
|
||||
load_fn(vk_device_wait_idle, "vkDeviceWaitIdle");
|
||||
load_fn(vk_create_fence, "vkCreateFence");
|
||||
load_fn(vk_destroy_fence, "vkDestroyFence");
|
||||
load_fn(vk_wait_for_fences, "vkWaitForFences");
|
||||
load_fn(vk_reset_fences, "vkResetFences");
|
||||
load_fn(vk_create_semaphore, "vkCreateSemaphore");
|
||||
load_fn(vk_destroy_semaphore, "vkDestroySemaphore");
|
||||
load_fn(vk_create_swapchain_khr, "vkCreateSwapchainKHR");
|
||||
load_fn(vk_destroy_swapchain_khr, "vkDestroySwapchainKHR");
|
||||
load_fn(vk_get_swapchain_images_khr, "vkGetSwapchainImagesKHR");
|
||||
load_fn(vk_acquire_next_image_khr, "vkAcquireNextImageKHR");
|
||||
load_fn(vk_queue_present_khr, "vkQueuePresentKHR");
|
||||
load_fn(vk_create_image_view, "vkCreateImageView");
|
||||
load_fn(vk_destroy_image_view, "vkDestroyImageView");
|
||||
load_fn(vk_create_render_pass, "vkCreateRenderPass");
|
||||
load_fn(vk_destroy_render_pass, "vkDestroyRenderPass");
|
||||
load_fn(vk_create_frame_buffer, "vkCreateFramebuffer");
|
||||
load_fn(vk_destroy_frame_buffer, "vkDestroyFramebuffer");
|
||||
load_fn(vk_create_shader_module, "vkCreateShaderModule");
|
||||
load_fn(vk_destroy_shader_module, "vkDestroyShaderModule");
|
||||
load_fn(vk_create_pipeline_layout, "vkCreatePipelineLayout");
|
||||
load_fn(vk_destroy_pipeline_layout, "vkDestroyPipelineLayout");
|
||||
load_fn(vk_create_graphics_pipelines, "vkCreateGraphicsPipelines");
|
||||
load_fn(vk_destroy_pipeline, "vkDestroyPipeline");
|
||||
load_fn(vk_cmd_begin_render_pass, "vkCmdBeginRenderPass");
|
||||
load_fn(vk_cmd_end_render_pass, "vkCmdEndRenderPass");
|
||||
load_fn(vk_cmd_bind_pipeline, "vkCmdBindPipeline");
|
||||
load_fn(vk_cmd_draw, "vkCmdDraw");
|
||||
load_fn(vk_cmd_set_viewport, "vkCmdSetViewport");
|
||||
load_fn(vk_cmd_set_scissors, "vkCmdSetScissor");
|
||||
}
|
||||
|
||||
auto parse_message_type(VkDebugUtilsMessageTypeFlagsEXT message_types) -> const char *
|
||||
{
|
||||
if (message_types == VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT)
|
||||
{
|
||||
return "GENERAL";
|
||||
}
|
||||
|
||||
if (message_types
|
||||
== (VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
|
||||
| VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT))
|
||||
{
|
||||
return "VALIDATION | PERFORMANCE";
|
||||
}
|
||||
|
||||
if (message_types == VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)
|
||||
{
|
||||
return "VALIDATION";
|
||||
}
|
||||
|
||||
return "PERFORMANCE";
|
||||
}
|
||||
|
||||
auto parse_message_severity(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity)
|
||||
-> app::SystemDiagnosis::Severity
|
||||
{
|
||||
using enum app::SystemDiagnosis::Severity;
|
||||
|
||||
switch (message_severity)
|
||||
{
|
||||
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: return verbose;
|
||||
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: return info;
|
||||
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: return warning;
|
||||
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: return error;
|
||||
default: ensure(false, "Invalid message severity: {}", static_cast<int>(message_severity));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto validation_layers_callback(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT const message_severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT const message_types,
|
||||
VkDebugUtilsMessengerCallbackDataEXT const *const callback_data,
|
||||
void *const vulkan_user_data
|
||||
) -> VkBool32
|
||||
{
|
||||
std::cout << callback_data->pMessage << std::endl;
|
||||
return VK_FALSE;
|
||||
|
||||
log_dbg("VALIDATION: {}", callback_data->pMessage);
|
||||
|
||||
ensure(vulkan_user_data, "Validation layers's user data is not set!");
|
||||
|
||||
auto stats = *(Ref<app::SystemStats> *)vulkan_user_data; // NOLINT
|
||||
|
||||
const auto &type = parse_message_type(message_types);
|
||||
|
||||
auto message = std::format(
|
||||
"Vulkan Validation Message:\ntype: {}\nseverity: {}\nmessage: {}",
|
||||
type,
|
||||
std::to_underlying(parse_message_severity(message_severity)),
|
||||
callback_data->pMessage
|
||||
);
|
||||
|
||||
auto severity = parse_message_severity(message_severity);
|
||||
if (std::to_underlying(severity) < 2)
|
||||
{
|
||||
return static_cast<VkBool32>(VK_FALSE);
|
||||
}
|
||||
|
||||
stats->push_diagnosis(
|
||||
app::SystemDiagnosis {
|
||||
.message = message,
|
||||
.code = {}, // TODO(Light): extract vulkan validation-layers code from the message
|
||||
.severity = severity,
|
||||
}
|
||||
);
|
||||
return static_cast<VkBool32>(VK_FALSE);
|
||||
}
|
||||
|
||||
} // namespace lt::renderer::vk
|
||||
63
modules/renderer/private/vk/context/instance.hpp
Normal file
63
modules/renderer/private/vk/context/instance.hpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
|
||||
#include <renderer/vk/vulkan.hpp>
|
||||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
/**
|
||||
* Responsible for dynamically loading Vulkan library/functions.
|
||||
*
|
||||
* @note: The delayed vkInstance destruction is due to a work-around for libx11:
|
||||
* @ref:
|
||||
* https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/commit/0017308648b6bf8eef10ef0ffb9470576c0c2e9e
|
||||
* https://www.xfree86.org/4.7.0/DRI11.html
|
||||
* https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/issues/1894
|
||||
*/
|
||||
class Instance
|
||||
{
|
||||
public:
|
||||
static auto get() -> VkInstance
|
||||
{
|
||||
return Instance::instance().m_instance;
|
||||
}
|
||||
|
||||
static auto load_device_functions(VkDevice device)
|
||||
{
|
||||
instance().load_device_functions_impl(device);
|
||||
}
|
||||
|
||||
Instance(Instance &&) = delete;
|
||||
|
||||
Instance(const Instance &) = delete;
|
||||
|
||||
auto operator=(const Instance &) -> Instance & = delete;
|
||||
|
||||
auto operator=(Instance &&) -> Instance & = delete;
|
||||
|
||||
private:
|
||||
static auto instance() -> Instance &
|
||||
{
|
||||
static auto instance = Instance {};
|
||||
return instance;
|
||||
}
|
||||
|
||||
Instance();
|
||||
|
||||
~Instance();
|
||||
|
||||
void initialize_instance();
|
||||
|
||||
void load_library();
|
||||
|
||||
void unload_library();
|
||||
|
||||
void load_global_functions();
|
||||
|
||||
void load_instance_functions();
|
||||
|
||||
void load_device_functions_impl(VkDevice device);
|
||||
|
||||
VkInstance m_instance = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
} // namespace lt::renderer::vk
|
||||
95
modules/renderer/private/vk/context/instance.test.cpp
Normal file
95
modules/renderer/private/vk/context/instance.test.cpp
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
#include <renderer/vk/context/instance.hpp>
|
||||
#include <test/test.hpp>
|
||||
|
||||
using namespace lt;
|
||||
using renderer::vk::Instance;
|
||||
using test::Case;
|
||||
using test::expect_not_nullptr;
|
||||
using test::Suite;
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
Suite raii = "raii"_suite = [] {
|
||||
Case { "post singleton insantiation state is correct" } = [] {
|
||||
expect_not_nullptr(Instance::get());
|
||||
|
||||
using namespace renderer::vk;
|
||||
expect_not_nullptr(vk_get_instance_proc_address);
|
||||
expect_not_nullptr(vk_create_instance);
|
||||
expect_not_nullptr(vk_enumerate_instance_extension_properties);
|
||||
expect_not_nullptr(vk_enumerate_instance_layer_properties);
|
||||
|
||||
expect_not_nullptr(vk_destroy_instance);
|
||||
expect_not_nullptr(vk_enumerate_physical_devices);
|
||||
expect_not_nullptr(vk_get_physical_device_properties);
|
||||
expect_not_nullptr(vk_get_physical_device_queue_family_properties);
|
||||
expect_not_nullptr(vk_create_device);
|
||||
expect_not_nullptr(vk_get_device_proc_address);
|
||||
expect_not_nullptr(vk_destroy_device);
|
||||
expect_not_nullptr(vk_get_physical_device_features);
|
||||
expect_not_nullptr(vk_enumerate_device_extension_properties);
|
||||
|
||||
expect_not_nullptr(vk_cmd_begin_debug_label);
|
||||
expect_not_nullptr(vk_cmd_end_debug_label);
|
||||
expect_not_nullptr(vk_cmd_insert_debug_label);
|
||||
expect_not_nullptr(vk_create_debug_messenger);
|
||||
expect_not_nullptr(vk_destroy_debug_messenger);
|
||||
expect_not_nullptr(vk_queue_begin_debug_label);
|
||||
expect_not_nullptr(vk_queue_end_debug_label);
|
||||
expect_not_nullptr(vk_queue_insert_debug_label);
|
||||
expect_not_nullptr(vk_set_debug_object_name);
|
||||
expect_not_nullptr(vk_set_debug_object_tag);
|
||||
expect_not_nullptr(vk_submit_debug_message);
|
||||
|
||||
expect_not_nullptr(vk_get_physical_device_surface_support);
|
||||
expect_not_nullptr(vk_get_physical_device_surface_capabilities);
|
||||
expect_not_nullptr(vk_get_physical_device_surface_formats);
|
||||
expect_not_nullptr(vk_create_xlib_surface_khr);
|
||||
expect_not_nullptr(vk_destroy_surface_khr);
|
||||
};
|
||||
|
||||
Case { "post load device functions state is correct" } = [] {
|
||||
using namespace renderer::vk;
|
||||
expect_not_nullptr(Instance::get());
|
||||
|
||||
expect_not_nullptr(vk_get_device_queue);
|
||||
expect_not_nullptr(vk_create_command_pool);
|
||||
expect_not_nullptr(vk_destroy_command_pool);
|
||||
expect_not_nullptr(vk_allocate_command_buffers);
|
||||
expect_not_nullptr(vk_free_command_buffers);
|
||||
expect_not_nullptr(vk_begin_command_buffer);
|
||||
expect_not_nullptr(vk_end_command_buffer);
|
||||
expect_not_nullptr(vk_cmd_pipeline_barrier);
|
||||
expect_not_nullptr(vk_queue_submit);
|
||||
expect_not_nullptr(vk_queue_wait_idle);
|
||||
expect_not_nullptr(vk_device_wait_idle);
|
||||
expect_not_nullptr(vk_create_fence);
|
||||
expect_not_nullptr(vk_destroy_fence);
|
||||
expect_not_nullptr(vk_wait_for_fences);
|
||||
expect_not_nullptr(vk_reset_fences);
|
||||
expect_not_nullptr(vk_create_semaphore);
|
||||
expect_not_nullptr(vk_destroy_semaphore);
|
||||
expect_not_nullptr(vk_create_swapchain_khr);
|
||||
expect_not_nullptr(vk_destroy_swapchain_khr);
|
||||
expect_not_nullptr(vk_get_swapchain_images_khr);
|
||||
expect_not_nullptr(vk_acquire_next_image_khr);
|
||||
expect_not_nullptr(vk_queue_present_khr);
|
||||
expect_not_nullptr(vk_create_image_view);
|
||||
expect_not_nullptr(vk_destroy_image_view);
|
||||
expect_not_nullptr(vk_create_render_pass);
|
||||
expect_not_nullptr(vk_destroy_render_pass);
|
||||
expect_not_nullptr(vk_create_frame_buffer);
|
||||
expect_not_nullptr(vk_destroy_frame_buffer);
|
||||
expect_not_nullptr(vk_create_shader_module);
|
||||
expect_not_nullptr(vk_destroy_shader_module);
|
||||
expect_not_nullptr(vk_create_pipeline_layout);
|
||||
expect_not_nullptr(vk_destroy_pipeline_layout);
|
||||
expect_not_nullptr(vk_create_graphics_pipelines);
|
||||
expect_not_nullptr(vk_destroy_pipeline);
|
||||
expect_not_nullptr(vk_cmd_begin_render_pass);
|
||||
expect_not_nullptr(vk_cmd_end_render_pass);
|
||||
expect_not_nullptr(vk_cmd_bind_pipeline);
|
||||
expect_not_nullptr(vk_cmd_draw);
|
||||
expect_not_nullptr(vk_cmd_set_viewport);
|
||||
expect_not_nullptr(vk_cmd_set_scissors);
|
||||
};
|
||||
};
|
||||
37
modules/renderer/private/vk/context/surface.cpp
Normal file
37
modules/renderer/private/vk/context/surface.cpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#include <renderer/vk/context/surface.hpp>
|
||||
#include <surface/components.hpp>
|
||||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
Surface::Surface(const ecs::Entity &surface_entity)
|
||||
{
|
||||
const auto &component = surface_entity.get<surface::SurfaceComponent>();
|
||||
|
||||
ensure(component.get_native_data().display, "Failed to initialize vk::Surface: null x-display");
|
||||
ensure(component.get_native_data().window, "Failed to initialize vk::Surface: null x-window");
|
||||
|
||||
auto create_info = VkXlibSurfaceCreateInfoKHR {
|
||||
.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
|
||||
.dpy = component.get_native_data().display,
|
||||
.window = component.get_native_data().window,
|
||||
};
|
||||
|
||||
auto *instance = Instance::get();
|
||||
auto result = vk_create_xlib_surface_khr(instance, &create_info, nullptr, &m_surface);
|
||||
|
||||
const auto &[width, height] = component.get_resolution();
|
||||
m_framebuffer_size = {
|
||||
.width = width,
|
||||
.height = height,
|
||||
};
|
||||
}
|
||||
|
||||
Surface::~Surface()
|
||||
{
|
||||
if (Instance::get())
|
||||
{
|
||||
vk_destroy_surface_khr(Instance::get(), m_surface, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lt::renderer::vk
|
||||
40
modules/renderer/private/vk/context/surface.hpp
Normal file
40
modules/renderer/private/vk/context/surface.hpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include <ecs/entity.hpp>
|
||||
#include <memory/pointer_types/null_on_move.hpp>
|
||||
#include <renderer/vk/context/instance.hpp>
|
||||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
class Surface
|
||||
{
|
||||
public:
|
||||
Surface(const ecs::Entity &surface_entity);
|
||||
|
||||
~Surface();
|
||||
|
||||
Surface(Surface &&) = default;
|
||||
|
||||
Surface(const Surface &) = delete;
|
||||
|
||||
auto operator=(Surface &&) -> Surface & = default;
|
||||
|
||||
auto operator=(const Surface &) const -> Surface & = delete;
|
||||
|
||||
[[nodiscard]] auto vk() const -> VkSurfaceKHR
|
||||
{
|
||||
return m_surface;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_framebuffer_size() const -> VkExtent2D
|
||||
{
|
||||
return m_framebuffer_size;
|
||||
}
|
||||
|
||||
private:
|
||||
memory::NullOnMove<VkSurfaceKHR> m_surface = VK_NULL_HANDLE;
|
||||
|
||||
VkExtent2D m_framebuffer_size {};
|
||||
};
|
||||
|
||||
} // namespace lt::renderer::vk
|
||||
51
modules/renderer/private/vk/context/surface.test.cpp
Normal file
51
modules/renderer/private/vk/context/surface.test.cpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#include <renderer/vk/context/surface.hpp>
|
||||
#include <renderer/vk/debug/messenger.hpp>
|
||||
#include <renderer/vk/test_utils.hpp>
|
||||
#include <surface/components.hpp>
|
||||
#include <surface/system.hpp>
|
||||
#include <test/test.hpp>
|
||||
|
||||
using ::lt::ecs::Entity;
|
||||
using ::lt::ecs::Registry;
|
||||
using ::lt::renderer::vk::Surface;
|
||||
using ::lt::surface::SurfaceComponent;
|
||||
using ::lt::surface::System;
|
||||
|
||||
Suite raii = "surface"_suite = [] {
|
||||
Case { "happy path won't throw" } = [&] {
|
||||
auto observer = ValidationObserver {};
|
||||
|
||||
auto registry = lt::create_ref<Registry>();
|
||||
auto entity = Entity { registry, registry->create_entity() };
|
||||
auto surface_system = System(registry);
|
||||
|
||||
entity.add<SurfaceComponent>(SurfaceComponent::CreateInfo {
|
||||
.title = "",
|
||||
.resolution = constants::resolution,
|
||||
.visible = true,
|
||||
});
|
||||
|
||||
const auto surface = Surface { entity };
|
||||
const auto &[x, y] = surface.get_framebuffer_size();
|
||||
|
||||
expect_eq(x, constants::resolution.x);
|
||||
expect_eq(y, constants::resolution.y);
|
||||
expect_not_nullptr(surface.vk());
|
||||
expect_false(observer.had_any_messages());
|
||||
};
|
||||
|
||||
Case { "unhappy path throws" } = [&] {
|
||||
auto observer = ValidationObserver {};
|
||||
auto registry = lt::create_ref<Registry>();
|
||||
auto entity = Entity { registry, registry->create_entity() };
|
||||
|
||||
entity.add<SurfaceComponent>(SurfaceComponent::CreateInfo {
|
||||
.title = "",
|
||||
.resolution = constants::resolution,
|
||||
.visible = true,
|
||||
});
|
||||
|
||||
expect_throw([&] { Surface { entity }; });
|
||||
expect_false(observer.had_any_messages());
|
||||
};
|
||||
};
|
||||
139
modules/renderer/private/vk/context/swapchain.cpp
Normal file
139
modules/renderer/private/vk/context/swapchain.cpp
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
#include <ranges>
|
||||
#include <renderer/vk/context/device.hpp>
|
||||
#include <renderer/vk/context/instance.hpp>
|
||||
#include <renderer/vk/context/surface.hpp>
|
||||
#include <renderer/vk/context/swapchain.hpp>
|
||||
#include <renderer/vk/debug/validation.hpp>
|
||||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
Swapchain::Swapchain(const Device &device, const Surface &surface): m_device(device.vk())
|
||||
{
|
||||
auto *physical_device = device.physical();
|
||||
|
||||
auto capabilities = VkSurfaceCapabilitiesKHR {};
|
||||
vkc(vk_get_physical_device_surface_capabilities(physical_device, surface.vk(), &capabilities));
|
||||
|
||||
auto count = uint32_t { 0 };
|
||||
vkc(vk_get_physical_device_surface_formats(physical_device, surface.vk(), &count, nullptr));
|
||||
|
||||
auto formats = std::vector<VkSurfaceFormatKHR>(count);
|
||||
vkc(vk_get_physical_device_surface_formats(
|
||||
physical_device,
|
||||
surface.vk(),
|
||||
&count,
|
||||
formats.data()
|
||||
));
|
||||
ensure(!formats.empty(), "Surface has no formats!");
|
||||
|
||||
// TODO(Light): parameterize
|
||||
constexpr auto desired_swapchain_image_count = uint32_t { 3 };
|
||||
const auto surface_format = formats.front();
|
||||
const auto queue_indices = device.get_family_indices();
|
||||
|
||||
auto create_info = VkSwapchainCreateInfoKHR {
|
||||
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
||||
.surface = surface.vk(),
|
||||
.minImageCount = get_optimal_image_count(capabilities, desired_swapchain_image_count),
|
||||
.imageFormat = surface_format.format,
|
||||
.imageColorSpace = surface_format.colorSpace,
|
||||
.imageExtent = surface.get_framebuffer_size(),
|
||||
.imageArrayLayers = 1u,
|
||||
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
||||
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = queue_indices.size(),
|
||||
.pQueueFamilyIndices = queue_indices.data(),
|
||||
.preTransform = capabilities.currentTransform,
|
||||
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
||||
.presentMode = VK_PRESENT_MODE_FIFO_RELAXED_KHR, // TODO(Light): parameterize
|
||||
.clipped = VK_TRUE,
|
||||
.oldSwapchain = nullptr,
|
||||
};
|
||||
|
||||
vkc(vk_create_swapchain_khr(device.vk(), &create_info, nullptr, &m_swapchain));
|
||||
vkc(vk_device_wait_idle(device.vk()));
|
||||
|
||||
auto image_count = uint32_t { 0u };
|
||||
vk_get_swapchain_images_khr(device.vk(), m_swapchain, &image_count, nullptr);
|
||||
|
||||
m_swapchain_images.resize(image_count);
|
||||
m_swapchain_image_views.resize(image_count);
|
||||
vk_get_swapchain_images_khr(device.vk(), m_swapchain, &image_count, m_swapchain_images.data());
|
||||
|
||||
for (auto [image, view] : std::views::zip(m_swapchain_images, m_swapchain_image_views))
|
||||
{
|
||||
auto create_info = VkImageViewCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = image,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = surface_format.format,
|
||||
.components = VkComponentMapping {
|
||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
},
|
||||
.subresourceRange = VkImageSubresourceRange {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0u,
|
||||
.levelCount = 1u,
|
||||
.baseArrayLayer = 0u,
|
||||
.layerCount = 1u,
|
||||
}
|
||||
};
|
||||
|
||||
vkc(vk_create_image_view(device.vk(), &create_info, nullptr, &view));
|
||||
}
|
||||
}
|
||||
|
||||
Swapchain::~Swapchain()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (m_device)
|
||||
{
|
||||
vkc(vk_device_wait_idle(m_device));
|
||||
for (auto &view : m_swapchain_image_views)
|
||||
{
|
||||
vk_destroy_image_view(m_device, view, nullptr);
|
||||
}
|
||||
|
||||
vk_destroy_swapchain_khr(m_device, m_swapchain, nullptr);
|
||||
}
|
||||
}
|
||||
catch (const std::exception &exp)
|
||||
{
|
||||
log_err("Failed to destroy swapchain:");
|
||||
log_err("\twhat: {}", exp.what());
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto Swapchain::get_optimal_image_count(
|
||||
VkSurfaceCapabilitiesKHR capabilities,
|
||||
uint32_t desired_image_count
|
||||
) const -> uint32_t
|
||||
{
|
||||
const auto min_image_count = capabilities.minImageCount;
|
||||
const auto max_image_count = capabilities.maxImageCount;
|
||||
|
||||
const auto has_max_limit = max_image_count != 0;
|
||||
|
||||
// Desired image count is in range
|
||||
if ((!has_max_limit || max_image_count >= desired_image_count)
|
||||
&& min_image_count <= desired_image_count)
|
||||
{
|
||||
return desired_image_count;
|
||||
}
|
||||
|
||||
// Fall-back to 2 if in ange
|
||||
if (min_image_count <= 2 && max_image_count >= 2)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Fall-back to min_image_count
|
||||
return min_image_count;
|
||||
}
|
||||
|
||||
|
||||
} // namespace lt::renderer::vk
|
||||
38
modules/renderer/private/vk/context/swapchain.hpp
Normal file
38
modules/renderer/private/vk/context/swapchain.hpp
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory/pointer_types/null_on_move.hpp>
|
||||
#include <renderer/vk/vulkan.hpp>
|
||||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
class Swapchain
|
||||
{
|
||||
public:
|
||||
Swapchain(const class Device &device, const class Surface &surface);
|
||||
|
||||
~Swapchain();
|
||||
|
||||
Swapchain(Swapchain &&) = default;
|
||||
|
||||
Swapchain(const Swapchain &) = delete;
|
||||
|
||||
auto operator=(Swapchain &&) -> Swapchain & = default;
|
||||
|
||||
auto operator=(const Swapchain &) const -> Swapchain & = delete;
|
||||
|
||||
private:
|
||||
[[nodiscard]] auto get_optimal_image_count(
|
||||
VkSurfaceCapabilitiesKHR capabilities,
|
||||
uint32_t desired_image_count
|
||||
) const -> uint32_t;
|
||||
|
||||
memory::NullOnMove<VkDevice> m_device;
|
||||
|
||||
memory::NullOnMove<VkSwapchainKHR> m_swapchain = VK_NULL_HANDLE;
|
||||
|
||||
std::vector<VkImage> m_swapchain_images;
|
||||
|
||||
std::vector<VkImageView> m_swapchain_image_views;
|
||||
};
|
||||
|
||||
} // namespace lt::renderer::vk
|
||||
143
modules/renderer/private/vk/debug/messenger.hpp
Normal file
143
modules/renderer/private/vk/debug/messenger.hpp
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory/pointer_types/null_on_move.hpp>
|
||||
#include <renderer/vk/context/instance.hpp>
|
||||
#include <renderer/vk/vulkan.hpp>
|
||||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
class Messenger
|
||||
{
|
||||
public:
|
||||
// NOLINTNEXTLINE(performance-enum-size)
|
||||
enum Severity : decltype(std::to_underlying(
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT
|
||||
))
|
||||
{
|
||||
verbose = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT,
|
||||
info = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT,
|
||||
warning = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT,
|
||||
error = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
|
||||
|
||||
all_severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT
|
||||
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
|
||||
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
|
||||
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(performance-enum-size)
|
||||
enum Type : decltype(std::to_underlying(VK_DEBUG_UTILS_MESSAGE_TYPE_FLAG_BITS_MAX_ENUM_EXT))
|
||||
{
|
||||
general = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT,
|
||||
validation = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
|
||||
performance = VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
|
||||
|
||||
// address_binding = VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT,
|
||||
|
||||
/** @note: does not include address binding yet. */
|
||||
all_type = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
|
||||
| VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
|
||||
| VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
|
||||
};
|
||||
|
||||
using CallbackData_T = const VkDebugUtilsMessengerCallbackDataEXT *;
|
||||
|
||||
using Callback_T = std::function<void(
|
||||
Severity message_severity,
|
||||
Type message_type,
|
||||
CallbackData_T vulkan_data,
|
||||
void *user_data
|
||||
)>;
|
||||
|
||||
struct CreateInfo
|
||||
{
|
||||
Severity severity;
|
||||
|
||||
Type type;
|
||||
|
||||
Callback_T callback;
|
||||
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
explicit Messenger(CreateInfo info)
|
||||
: m_callback(std::move(info.callback))
|
||||
, m_user_data(info.user_data)
|
||||
{
|
||||
ensure(m_callback, "Failed to initialize vk::Messenger: null callback");
|
||||
ensure(info.severity != Severity {}, "Failed to initialize vk::Messenger: null severity");
|
||||
ensure(info.type != Type {}, "Failed to initialize vk::Messenger: null type");
|
||||
|
||||
// Instance may not be initialized yet
|
||||
// Making it get initialized inside a call to vk_create_debug_messenger
|
||||
// Which would invoke a nullptr...
|
||||
if (!vk_create_debug_messenger)
|
||||
{
|
||||
Instance::get();
|
||||
}
|
||||
|
||||
const auto vulkan_info = VkDebugUtilsMessengerCreateInfoEXT {
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
|
||||
.messageSeverity = info.severity,
|
||||
.messageType = info.type,
|
||||
.pfnUserCallback = &validation_layers_callback,
|
||||
.pUserData = this,
|
||||
};
|
||||
|
||||
ensure(
|
||||
!vk_create_debug_messenger(Instance::get(), &vulkan_info, nullptr, &m_debug_messenger),
|
||||
"Failed to create vulkan debug utils messenger"
|
||||
);
|
||||
}
|
||||
|
||||
~Messenger()
|
||||
{
|
||||
vk_destroy_debug_messenger(Instance::get(), m_debug_messenger, nullptr);
|
||||
}
|
||||
|
||||
Messenger(Messenger &&) = default;
|
||||
|
||||
Messenger(const Messenger &) = delete;
|
||||
|
||||
auto operator=(Messenger &&) -> Messenger & = default;
|
||||
|
||||
auto operator=(const Messenger &) const -> Messenger & = delete;
|
||||
|
||||
|
||||
private:
|
||||
static auto validation_layers_callback(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT const message_severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT const message_type,
|
||||
VkDebugUtilsMessengerCallbackDataEXT const *const callback_data,
|
||||
void *const vulkan_user_data
|
||||
) -> VkBool32
|
||||
{
|
||||
auto *messenger = (Messenger *)vulkan_user_data; // NOLINT
|
||||
ensure(messenger, "Null vulkan_user_data received in messenger callback");
|
||||
|
||||
messenger->validation_layers_callback_impl(message_severity, message_type, callback_data);
|
||||
return VK_FALSE;
|
||||
}
|
||||
|
||||
void validation_layers_callback_impl(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT message_type,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT *callback_data
|
||||
)
|
||||
{
|
||||
m_callback(
|
||||
static_cast<Severity>(message_severity),
|
||||
static_cast<Type>(message_type),
|
||||
callback_data,
|
||||
m_user_data
|
||||
);
|
||||
}
|
||||
|
||||
memory::NullOnMove<VkDebugUtilsMessengerEXT> m_debug_messenger = VK_NULL_HANDLE;
|
||||
|
||||
Callback_T m_callback;
|
||||
|
||||
void *m_user_data;
|
||||
};
|
||||
|
||||
} // namespace lt::renderer::vk
|
||||
149
modules/renderer/private/vk/debug/messenger.test.cpp
Normal file
149
modules/renderer/private/vk/debug/messenger.test.cpp
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
#include <renderer/vk/context/surface.hpp>
|
||||
#include <renderer/vk/debug/messenger.hpp>
|
||||
#include <renderer/vk/test_utils.hpp>
|
||||
#include <surface/components.hpp>
|
||||
#include <surface/system.hpp>
|
||||
#include <test/expects.hpp>
|
||||
#include <test/test.hpp>
|
||||
|
||||
using namespace lt;
|
||||
using renderer::vk::Messenger;
|
||||
using renderer::vk::Surface;
|
||||
using enum Messenger::Severity;
|
||||
using enum Messenger::Type;
|
||||
|
||||
void noop_callback(
|
||||
Messenger::Severity message_severity,
|
||||
Messenger::Type message_type,
|
||||
Messenger::CallbackData_T vulkan_data,
|
||||
void *user_data
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
Suite raii = "messenger_raii"_suite = [] {
|
||||
Case { "happy path won't throw" } = [] {
|
||||
Messenger(
|
||||
Messenger::CreateInfo {
|
||||
.severity = all_severity,
|
||||
.type = all_type,
|
||||
.callback = &noop_callback,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
Case { "unhappy path throws" } = [] {
|
||||
expect_throw([] {
|
||||
Messenger(
|
||||
Messenger::CreateInfo {
|
||||
.severity = all_severity,
|
||||
.type = all_type,
|
||||
.callback = {},
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
expect_throw([] {
|
||||
Messenger(
|
||||
Messenger::CreateInfo {
|
||||
.severity = {},
|
||||
.type = all_type,
|
||||
.callback = &noop_callback,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
expect_throw([] {
|
||||
Messenger(
|
||||
Messenger::CreateInfo {
|
||||
.severity = all_severity,
|
||||
.type = {},
|
||||
.callback = &noop_callback,
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-interfaces-global-init)
|
||||
Suite callback = "messenger_callback"_suite = [] {
|
||||
Case { "callback gets called with correct arguments" } = [] {
|
||||
auto total_messages = 0u;
|
||||
auto first_message_is_severity = false;
|
||||
auto second_message_is_type = false;
|
||||
auto third_message_is_user_callback = false;
|
||||
auto all_messages_are_error = true;
|
||||
auto all_messages_are_validation = true;
|
||||
auto user_data_is_always_69 = true;
|
||||
|
||||
auto callback = [&](Messenger::Severity message_severity,
|
||||
Messenger::Type message_type,
|
||||
Messenger::CallbackData_T vulkan_data,
|
||||
void *user_data) {
|
||||
++total_messages;
|
||||
auto message = std::string_view { vulkan_data->pMessage };
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
|
||||
user_data_is_always_69 = user_data_is_always_69 && *(size_t *)user_data == 69u;
|
||||
|
||||
all_messages_are_validation = all_messages_are_validation && message_type == validation;
|
||||
all_messages_are_error = all_messages_are_error && message_severity == error;
|
||||
|
||||
if (total_messages == 1)
|
||||
{
|
||||
first_message_is_severity = message.starts_with(
|
||||
"vkCreateDebugUtilsMessengerEXT(): pCreateInfo->messageSeverity is zero."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (total_messages == 2)
|
||||
{
|
||||
second_message_is_type = message.starts_with(
|
||||
"vkCreateDebugUtilsMessengerEXT(): pCreateInfo->messageType is zero."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (total_messages == 3)
|
||||
{
|
||||
third_message_is_user_callback = message.starts_with(
|
||||
"vkCreateDebugUtilsMessengerEXT(): pCreateInfo->pfnUserCallback is NULL."
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
auto user_data = size_t { 69 };
|
||||
auto messenger = Messenger(
|
||||
Messenger::CreateInfo {
|
||||
.severity = all_severity,
|
||||
.type = all_type,
|
||||
.callback = callback,
|
||||
.user_data = &user_data,
|
||||
}
|
||||
);
|
||||
|
||||
{
|
||||
auto *messenger = VkDebugUtilsMessengerEXT {};
|
||||
auto info = VkDebugUtilsMessengerCreateInfoEXT {
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
|
||||
};
|
||||
renderer::vk::vk_create_debug_messenger(
|
||||
renderer::vk::Instance::get(),
|
||||
&info,
|
||||
nullptr,
|
||||
&messenger
|
||||
);
|
||||
}
|
||||
|
||||
expect_eq(total_messages, 3u);
|
||||
expect_true(first_message_is_severity);
|
||||
expect_true(second_message_is_type);
|
||||
expect_true(third_message_is_user_callback);
|
||||
expect_true(all_messages_are_error);
|
||||
expect_true(all_messages_are_validation);
|
||||
expect_true(user_data_is_always_69);
|
||||
};
|
||||
};
|
||||
17
modules/renderer/private/vk/debug/validation.hpp
Normal file
17
modules/renderer/private/vk/debug/validation.hpp
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <renderer/vk/vulkan.hpp>
|
||||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
inline void vkc(VkResult result)
|
||||
{
|
||||
if (result)
|
||||
{
|
||||
throw std::runtime_error {
|
||||
std::format("Vulkan call failed with result: {}", std::to_underlying(result))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lt::renderer::vk
|
||||
18
modules/renderer/private/vk/pipeline.cpp
Normal file
18
modules/renderer/private/vk/pipeline.cpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#include <renderer/vk/pipeline.hpp>
|
||||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
Pipeline::Pipeline(CreateInfo info): m_context(std::move(info.context))
|
||||
{
|
||||
ensure(m_context, "Failed to create vk pipeline: null context");
|
||||
}
|
||||
|
||||
Pipeline::~Pipeline()
|
||||
{
|
||||
if (m_context)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lt::renderer::vk
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue