basic rendering
This commit is contained in:
19
assets/shaders/test.slang
Normal file
19
assets/shaders/test.slang
Normal file
@@ -0,0 +1,19 @@
|
||||
struct post_transform_vertex {
|
||||
float4 position : SV_Position;
|
||||
float2 uv : TEXCOORD;
|
||||
};
|
||||
|
||||
[shader("vertex")]
|
||||
post_transform_vertex vert_main(uint vertex_id : SV_VertexID) {
|
||||
post_transform_vertex output;
|
||||
|
||||
output.uv = float2((vertex_id << 1) & 2, vertex_id & 2);
|
||||
output.position = float4(output.uv * 2.0f - 1.0f, 0.0f, 1.0f);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
[shader("fragment")]
|
||||
float4 frag_main(post_transform_vertex input) : SV_Target {
|
||||
return float4(1.0f);
|
||||
}
|
||||
3
external/CMakeLists.txt
vendored
3
external/CMakeLists.txt
vendored
@@ -1,7 +1,8 @@
|
||||
include(FetchContent)
|
||||
|
||||
set(VUK_USE_SHADERC OFF CACHE BOOL "NO SHADERC" FORCE)
|
||||
set(VUK_EXTRA_IMGUI OFF CACHE BOOL "WAYLAND GLFW" FORCE)
|
||||
set(VUK_EXTRA_IMGUI ON CACHE BOOL "VUK IMGUI" FORCE)
|
||||
set(VUK_IMGUI_PLATFORM_BACKEND "glfw" CACHE STRING "USE IMGUI VUK GLFW" FORCE)
|
||||
FetchContent_Declare(
|
||||
vuk_fetch
|
||||
GIT_REPOSITORY https://github.com/martty/vuk.git
|
||||
|
||||
@@ -7,6 +7,8 @@ add_library(rogue-lib STATIC
|
||||
ui/user_input.hpp
|
||||
render/context.cpp
|
||||
render/context.hpp
|
||||
render/shader_compiler.cpp
|
||||
render/shader_compiler.hpp
|
||||
rogue.cpp
|
||||
rogue.hpp
|
||||
)
|
||||
@@ -15,6 +17,7 @@ target_compile_definitions(rogue-lib PUBLIC -DGLFW_INCLUDE_VULKAN -DGLM_ENABLE_E
|
||||
target_link_libraries(rogue-lib PUBLIC
|
||||
glm
|
||||
vuk
|
||||
vuk-extra-imgui-platform
|
||||
vk-bootstrap
|
||||
slang
|
||||
fastgltf
|
||||
|
||||
220
source/render/shader_compiler.cpp
Normal file
220
source/render/shader_compiler.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
#include "shader_compiler.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <slang-com-ptr.h>
|
||||
#include <slang.h>
|
||||
|
||||
namespace rog::render {
|
||||
|
||||
void shader_compiler::destroy_slang(ISlangUnknown *object) {
|
||||
object->release();
|
||||
}
|
||||
|
||||
shader_compiler::shader_compiler(std::filesystem::path root) : root(root) {
|
||||
const auto slang_global_session_description = SlangGlobalSessionDesc{};
|
||||
slang::IGlobalSession *global_session_ptr = nullptr;
|
||||
[[maybe_unused]] const auto result = slang::createGlobalSession(
|
||||
&slang_global_session_description, &global_session_ptr);
|
||||
global_session = slang_unique_ptr<slang::IGlobalSession>(global_session_ptr,
|
||||
destroy_slang);
|
||||
|
||||
auto paths_as_c_strings = std::vector<const char *>();
|
||||
for (const auto &path : search_paths) {
|
||||
paths_as_c_strings.push_back(path.generic_string().c_str());
|
||||
}
|
||||
|
||||
auto profile = global_session->findProfile("spirv_1_4");
|
||||
|
||||
auto target = slang::TargetDesc{
|
||||
.format = SlangCompileTarget::SLANG_SPIRV,
|
||||
.profile = profile,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
auto optimisation_level = slang::CompilerOptionEntry{
|
||||
.name = slang::CompilerOptionName::Optimization,
|
||||
.value = {
|
||||
.intValue0 = SlangOptimizationLevel::SLANG_OPTIMIZATION_LEVEL_MAXIMAL,
|
||||
}};
|
||||
auto debug_info = slang::CompilerOptionEntry{
|
||||
.name = slang::CompilerOptionName::DebugInformation,
|
||||
.value = {
|
||||
.intValue0 = SLANG_DEBUG_INFO_LEVEL_MAXIMAL,
|
||||
}};
|
||||
auto line_directive = slang::CompilerOptionEntry{
|
||||
.name = slang::CompilerOptionName::LineDirectiveMode,
|
||||
.value = {
|
||||
.intValue0 = SLANG_LINE_DIRECTIVE_MODE_SOURCE_MAP,
|
||||
}};
|
||||
auto name_main = slang::CompilerOptionEntry{
|
||||
.name = slang::CompilerOptionName::VulkanUseEntryPointName,
|
||||
.value = {
|
||||
.intValue0 = 1,
|
||||
}};
|
||||
|
||||
auto infos =
|
||||
std::array{optimisation_level, debug_info, line_directive, name_main};
|
||||
|
||||
auto slang_session_descriptor = slang::SessionDesc{};
|
||||
slang_session_descriptor.searchPaths = paths_as_c_strings.data();
|
||||
slang_session_descriptor.searchPathCount =
|
||||
static_cast<int>(paths_as_c_strings.size());
|
||||
slang_session_descriptor.targets = ⌖
|
||||
slang_session_descriptor.targetCount = 1;
|
||||
slang_session_descriptor.defaultMatrixLayoutMode =
|
||||
SLANG_MATRIX_LAYOUT_COLUMN_MAJOR;
|
||||
slang_session_descriptor.compilerOptionEntries = infos.data();
|
||||
slang_session_descriptor.compilerOptionEntryCount =
|
||||
static_cast<std::int32_t>(infos.size());
|
||||
|
||||
slang::ISession *session_ptr = nullptr;
|
||||
global_session->createSession(slang_session_descriptor, &session_ptr);
|
||||
session = slang_unique_ptr<slang::ISession>(session_ptr, destroy_slang);
|
||||
}
|
||||
|
||||
std::vector<std::uint32_t>
|
||||
shader_compiler::compile(const std::string_view module_name,
|
||||
const std::string_view entry_name) {
|
||||
const auto initial_path = root / module_name;
|
||||
|
||||
const auto module = load_module(initial_path.generic_string());
|
||||
const auto entry_point = find_entry_point(module, entry_name);
|
||||
const auto composed_program = create_composed_program(module, entry_point);
|
||||
const auto spirv_blob = compile_to_spirv(composed_program.get());
|
||||
|
||||
auto data = std::vector<std::uint32_t>(spirv_blob->getBufferSize() / 4);
|
||||
std::memcpy(data.data(), spirv_blob->getBufferPointer(), data.size() * 4);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
slang::IModule *
|
||||
shader_compiler::load_module(const std::string_view module_name) {
|
||||
slang::IBlob *module_blob_ptr = nullptr;
|
||||
auto module = session->loadModule(module_name.data(), &module_blob_ptr);
|
||||
auto module_blob =
|
||||
slang_unique_ptr<slang::IBlob>(module_blob_ptr, destroy_slang);
|
||||
|
||||
if (!module) {
|
||||
throw_error("loading module", module_blob.get());
|
||||
}
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
void shader_compiler::throw_error(const std::string_view context,
|
||||
slang::IBlob *diagnostic_blob) {
|
||||
const auto char_ptr =
|
||||
reinterpret_cast<const char *>(diagnostic_blob->getBufferPointer());
|
||||
|
||||
const auto message = std::format("{}: {}", context, char_ptr);
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
slang::IEntryPoint *
|
||||
shader_compiler::find_entry_point(slang::IModule *module,
|
||||
const std::string_view entry_point_name) {
|
||||
slang::IEntryPoint *entry_point;
|
||||
if (module->findEntryPointByName(entry_point_name.data(), &entry_point) !=
|
||||
SLANG_OK) {
|
||||
throw_error(std::format("finding entry point {}", entry_point_name));
|
||||
}
|
||||
return entry_point;
|
||||
}
|
||||
|
||||
void shader_compiler::throw_error(const std::string_view context) {
|
||||
throw std::runtime_error(std::string(context));
|
||||
}
|
||||
shader_compiler::slang_unique_ptr<slang::IComponentType>
|
||||
shader_compiler::create_composed_program(slang::IModule *module,
|
||||
slang::IEntryPoint *entry_point) {
|
||||
slang::IBlob *diagnostic_blob_ptr = nullptr;
|
||||
|
||||
auto component_types = std::vector<slang::IComponentType *>();
|
||||
component_types.push_back(module);
|
||||
component_types.push_back(entry_point);
|
||||
|
||||
slang::IComponentType *composed_program = nullptr;
|
||||
|
||||
if (session->createCompositeComponentType(
|
||||
component_types.data(), component_types.size(), &composed_program,
|
||||
&diagnostic_blob_ptr) != SLANG_OK) {
|
||||
throw_error("creating composed program", diagnostic_blob_ptr);
|
||||
}
|
||||
|
||||
return slang_unique_ptr<slang::IComponentType>(composed_program,
|
||||
destroy_slang);
|
||||
}
|
||||
shader_compiler::slang_unique_ptr<slang::IBlob>
|
||||
shader_compiler::compile_to_spirv(slang::IComponentType *composed_program) {
|
||||
slang::IBlob *diagnostic_blob_ptr = nullptr;
|
||||
|
||||
slang::IBlob *spirv_blob = nullptr;
|
||||
|
||||
composed_program->getEntryPointCode(0, 0, &spirv_blob, &diagnostic_blob_ptr);
|
||||
if (!spirv_blob) {
|
||||
throw_error("compiling to spirv", diagnostic_blob_ptr);
|
||||
}
|
||||
|
||||
return slang_unique_ptr<slang::IBlob>(spirv_blob, destroy_slang);
|
||||
}
|
||||
void shader_compiler::reload_session() {
|
||||
auto paths_as_c_strings = std::vector<const char *>();
|
||||
for (const auto &path : search_paths) {
|
||||
paths_as_c_strings.push_back(path.generic_string().c_str());
|
||||
}
|
||||
|
||||
auto profile = global_session->findProfile("spirv_1_4");
|
||||
|
||||
auto target = slang::TargetDesc{
|
||||
.format = SlangCompileTarget::SLANG_SPIRV,
|
||||
.profile = profile,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
auto optimisation_level = slang::CompilerOptionEntry{
|
||||
.name = slang::CompilerOptionName::Optimization,
|
||||
.value = {
|
||||
.intValue0 = SlangOptimizationLevel::SLANG_OPTIMIZATION_LEVEL_NONE,
|
||||
}};
|
||||
auto debug_info = slang::CompilerOptionEntry{
|
||||
.name = slang::CompilerOptionName::DebugInformation,
|
||||
.value = {
|
||||
.intValue0 = SLANG_DEBUG_INFO_LEVEL_MAXIMAL,
|
||||
}};
|
||||
auto line_directive = slang::CompilerOptionEntry{
|
||||
.name = slang::CompilerOptionName::LineDirectiveMode,
|
||||
.value = {
|
||||
.intValue0 = SLANG_LINE_DIRECTIVE_MODE_SOURCE_MAP,
|
||||
}};
|
||||
auto name_main = slang::CompilerOptionEntry{
|
||||
.name = slang::CompilerOptionName::VulkanUseEntryPointName,
|
||||
.value = {
|
||||
.intValue0 = 1,
|
||||
}};
|
||||
|
||||
auto infos =
|
||||
std::array{optimisation_level, debug_info, line_directive, name_main};
|
||||
|
||||
auto slang_session_descriptor = slang::SessionDesc{};
|
||||
slang_session_descriptor.searchPaths = paths_as_c_strings.data();
|
||||
slang_session_descriptor.searchPathCount =
|
||||
static_cast<int>(paths_as_c_strings.size());
|
||||
slang_session_descriptor.targets = ⌖
|
||||
slang_session_descriptor.targetCount = 1;
|
||||
slang_session_descriptor.defaultMatrixLayoutMode =
|
||||
SLANG_MATRIX_LAYOUT_COLUMN_MAJOR;
|
||||
slang_session_descriptor.compilerOptionEntries = infos.data();
|
||||
slang_session_descriptor.compilerOptionEntryCount =
|
||||
static_cast<std::int32_t>(infos.size());
|
||||
|
||||
slang::ISession *session_ptr = nullptr;
|
||||
global_session->createSession(slang_session_descriptor, &session_ptr);
|
||||
session = slang_unique_ptr<slang::ISession>(session_ptr, destroy_slang);
|
||||
}
|
||||
|
||||
} // namespace rog::render
|
||||
56
source/render/shader_compiler.hpp
Normal file
56
source/render/shader_compiler.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#ifndef SHADER_COMPILER_HPP
|
||||
#define SHADER_COMPILER_HPP
|
||||
|
||||
#include <filesystem>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <slang.h>
|
||||
|
||||
namespace rog::render {
|
||||
|
||||
class shader_compiler {
|
||||
public:
|
||||
explicit shader_compiler(std::filesystem::path root);
|
||||
|
||||
[[nodiscard]] std::vector<std::uint32_t>
|
||||
compile(const std::string_view module_name,
|
||||
const std::string_view entry_name = "main");
|
||||
|
||||
void reload_session();
|
||||
|
||||
private:
|
||||
static void destroy_slang(ISlangUnknown *object);
|
||||
template <typename T>
|
||||
using slang_unique_ptr = std::unique_ptr<T, decltype(&destroy_slang)>;
|
||||
|
||||
std::filesystem::path root;
|
||||
|
||||
slang_unique_ptr<slang::IGlobalSession> global_session = {nullptr,
|
||||
destroy_slang};
|
||||
slang_unique_ptr<slang::ISession> session = {nullptr, destroy_slang};
|
||||
|
||||
std::vector<std::filesystem::path> search_paths = {"assets/shaders"};
|
||||
|
||||
[[nodiscard]] slang::IModule *load_module(const std::string_view module_name);
|
||||
|
||||
[[nodiscard]] slang::IEntryPoint *
|
||||
find_entry_point(slang::IModule *module,
|
||||
const std::string_view entry_point_name);
|
||||
|
||||
[[nodiscard]] slang_unique_ptr<slang::IComponentType>
|
||||
create_composed_program(slang::IModule *module,
|
||||
slang::IEntryPoint *entry_point);
|
||||
|
||||
[[nodiscard]] slang_unique_ptr<slang::IBlob>
|
||||
compile_to_spirv(slang::IComponentType *composed_program);
|
||||
|
||||
[[noreturn]] void throw_error(const std::string_view context,
|
||||
slang::IBlob *diagnostic_blob);
|
||||
|
||||
[[noreturn]] void throw_error(const std::string_view context);
|
||||
};
|
||||
|
||||
} // namespace rog::render
|
||||
|
||||
#endif
|
||||
119
source/rogue.cpp
119
source/rogue.cpp
@@ -1,23 +1,140 @@
|
||||
#include "rogue.hpp"
|
||||
|
||||
#include "vuk/ImageAttachment.hpp"
|
||||
#include "vuk/RenderGraph.hpp"
|
||||
#include "vuk/Types.hpp"
|
||||
#include "vuk/Value.hpp"
|
||||
#include "vuk/runtime/CommandBuffer.hpp"
|
||||
#include "vuk/runtime/vk/Allocator.hpp"
|
||||
#include "vuk/runtime/vk/DeviceFrameResource.hpp"
|
||||
#include "vuk/runtime/vk/Image.hpp"
|
||||
#include "vuk/runtime/vk/VkRuntime.hpp"
|
||||
#include "vuk/vsl/Core.hpp"
|
||||
#include <backends/imgui_impl_glfw.h>
|
||||
#include <vuk/extra/ImGuiIntegration.hpp>
|
||||
#include <vuk/runtime/vk/Pipeline.hpp>
|
||||
#include <vuk/runtime/vk/PipelineTypes.hpp>
|
||||
#include <vuk/vsl/BindlessArray.hpp>
|
||||
|
||||
[[nodiscard]] auto composite() {
|
||||
return vuk::make_pass("composite", [](vuk::CommandBuffer &command_buffer,
|
||||
VUK_IA(vuk::eColorWrite) target) {
|
||||
command_buffer.bind_graphics_pipeline("test")
|
||||
.set_dynamic_state(vuk::DynamicStateFlagBits::eScissor |
|
||||
vuk::DynamicStateFlagBits::eViewport)
|
||||
.set_viewport(0, vuk::Rect2D::framebuffer())
|
||||
.set_scissor(0, vuk::Rect2D::framebuffer())
|
||||
.set_rasterization({})
|
||||
.set_depth_stencil({
|
||||
.depthTestEnable = false,
|
||||
.depthWriteEnable = false,
|
||||
.depthCompareOp = vuk::CompareOp::eLess,
|
||||
})
|
||||
.set_color_blend(target, {})
|
||||
.draw(3, 1, 0, 0);
|
||||
|
||||
return std::make_tuple(std::move(target));
|
||||
});
|
||||
}
|
||||
|
||||
namespace rog {
|
||||
|
||||
rogue::rogue(logger *log)
|
||||
: log(log), display(&user_input, {1920, 1080}, "Rogue PT"),
|
||||
context(&display) {
|
||||
context(&display), shader_compiler("../assets/shaders") {
|
||||
create_pipelines();
|
||||
if (!context.rtx_supported) {
|
||||
log->warn("rtx is not supported on {}. inline ray tracing and ray tracing "
|
||||
"pipeline will not be available",
|
||||
context.gpu_name);
|
||||
}
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
|
||||
ImGui_ImplGlfw_InitForOther(display.raw(), true);
|
||||
imgui_data = vuk::extra::ImGui_ImplVuk_Init(context.superframe_allocator);
|
||||
}
|
||||
|
||||
void rogue::stop() { close_requested = true; }
|
||||
|
||||
void rogue::do_tick() {
|
||||
auto [frame_allocator, frame_target] = get_next_frame_resources();
|
||||
|
||||
std::tie(frame_target) = composite()(std::move(frame_target));
|
||||
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
ImGui::Begin("Test");
|
||||
ImGui::Text("Hello World!");
|
||||
ImGui::End();
|
||||
ImGui::Render();
|
||||
|
||||
frame_target = vuk::extra::ImGui_ImplVuk_Render(
|
||||
frame_allocator, std::move(frame_target), imgui_data);
|
||||
|
||||
auto entire_thing = vuk::enqueue_presentation(frame_target);
|
||||
entire_thing.submit(frame_allocator, context.compiler);
|
||||
}
|
||||
|
||||
void rogue::run() {
|
||||
while (!close_requested.load() && !display.should_close()) {
|
||||
do_tick();
|
||||
|
||||
display.poll_events();
|
||||
}
|
||||
}
|
||||
|
||||
vuk::PipelineBaseCreateInfo
|
||||
rogue::create_compute_pipeline(std::string_view comp_module,
|
||||
std::string_view comp_entry) {
|
||||
const auto comp_bytes = shader_compiler.compile(comp_module, comp_entry);
|
||||
|
||||
auto pipeline_info = vuk::PipelineBaseCreateInfo();
|
||||
pipeline_info.add_spirv(comp_bytes, comp_module.data(), comp_entry.data());
|
||||
|
||||
return pipeline_info;
|
||||
}
|
||||
|
||||
vuk::PipelineBaseCreateInfo rogue::create_graphics_pipeline(
|
||||
std::string_view vs_module, std::string_view vs_entry,
|
||||
std::string_view fs_module, std::string_view fs_entry) {
|
||||
const auto vs_bytes = shader_compiler.compile(vs_module, vs_entry);
|
||||
const auto fs_bytes = shader_compiler.compile(fs_module, fs_entry);
|
||||
|
||||
auto pipeline_info = vuk::PipelineBaseCreateInfo();
|
||||
pipeline_info.add_spirv(vs_bytes, vs_module.data(), vs_entry.data());
|
||||
pipeline_info.add_spirv(fs_bytes, fs_module.data(), fs_entry.data());
|
||||
return pipeline_info;
|
||||
}
|
||||
|
||||
vuk::PipelineBaseCreateInfo
|
||||
rogue::create_graphics_pipeline(std::string_view module_name) {
|
||||
return create_graphics_pipeline(module_name, "vert_main", module_name,
|
||||
"frag_main");
|
||||
}
|
||||
|
||||
void rogue::create_pipelines() {
|
||||
log->debug("creating pipelines");
|
||||
context.runtime.create_named_pipeline("test",
|
||||
create_graphics_pipeline("test.slang"));
|
||||
log->debug("created pipelines");
|
||||
}
|
||||
|
||||
vuk::Value<vuk::ImageAttachment> rogue::get_swap_target() {
|
||||
auto imported_swapchain = vuk::acquire_swapchain(context.swapchain);
|
||||
auto swapchain_image =
|
||||
vuk::acquire_next_image("swp_img", std::move(imported_swapchain));
|
||||
return vuk::clear_image(std::move(swapchain_image),
|
||||
vuk::ClearColor{0.0f, 0.0f, 1.0f, 1.0f});
|
||||
}
|
||||
|
||||
rogue::vuk_frame_resources rogue::get_next_frame_resources() {
|
||||
auto &frame_resource = context.superframe_resource.get_next_frame();
|
||||
context.runtime.next_frame();
|
||||
return {
|
||||
.allocator = vuk::Allocator(frame_resource),
|
||||
.swap_target = get_swap_target(),
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace rog
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
#include <atomic>
|
||||
|
||||
#include "render/context.hpp"
|
||||
#include "render/shader_compiler.hpp"
|
||||
#include "ui/display.hpp"
|
||||
#include "ui/user_input.hpp"
|
||||
#include "util/logger.hpp"
|
||||
#include "vuk/extra/ImGuiIntegration.hpp"
|
||||
|
||||
namespace rog {
|
||||
|
||||
@@ -16,9 +18,35 @@ private:
|
||||
ui::user_input user_input = {};
|
||||
ui::display display;
|
||||
render::context context;
|
||||
render::shader_compiler shader_compiler;
|
||||
|
||||
std::atomic<bool> close_requested = false;
|
||||
|
||||
vuk::extra::ImGuiData imgui_data;
|
||||
|
||||
[[nodiscard]] vuk::PipelineBaseCreateInfo
|
||||
create_compute_pipeline(std::string_view comp_module,
|
||||
std::string_view comp_entry);
|
||||
|
||||
[[nodiscard]] vuk::PipelineBaseCreateInfo create_graphics_pipeline(
|
||||
std::string_view vs_module, std::string_view vs_entry,
|
||||
std::string_view fs_module, std::string_view fs_entry);
|
||||
|
||||
[[nodiscard]] vuk::PipelineBaseCreateInfo
|
||||
create_graphics_pipeline(std::string_view module);
|
||||
|
||||
void create_pipelines();
|
||||
|
||||
[[nodiscard]] vuk::Value<vuk::ImageAttachment> get_swap_target();
|
||||
|
||||
struct vuk_frame_resources {
|
||||
vuk::Allocator allocator;
|
||||
vuk::Value<vuk::ImageAttachment> swap_target;
|
||||
};
|
||||
[[nodiscard]] vuk_frame_resources get_next_frame_resources();
|
||||
|
||||
void do_tick();
|
||||
|
||||
public:
|
||||
explicit rogue(logger *log);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user