添加渲染管线实现及着色器文件热重载功能
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -28,3 +28,6 @@
|
|||||||
[submodule "third_party/fmt"]
|
[submodule "third_party/fmt"]
|
||||||
path = third_party/fmt
|
path = third_party/fmt
|
||||||
url = https://github.com/fmtlib/fmt.git
|
url = https://github.com/fmtlib/fmt.git
|
||||||
|
[submodule "third_party/efsw"]
|
||||||
|
path = third_party/efsw
|
||||||
|
url = https://github.com/SpartanJ/efsw.git
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ add_subdirectory(third_party/SDL)
|
|||||||
add_subdirectory(third_party/spdlog)
|
add_subdirectory(third_party/spdlog)
|
||||||
add_subdirectory(third_party/stb)
|
add_subdirectory(third_party/stb)
|
||||||
add_subdirectory(third_party/yoga/yoga)
|
add_subdirectory(third_party/yoga/yoga)
|
||||||
|
add_subdirectory(third_party/efsw)
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
add_subdirectory(example)
|
add_subdirectory(example)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ target_link_libraries(${PROJECT_NAME} PUBLIC
|
|||||||
stb
|
stb
|
||||||
fmt::fmt-header-only
|
fmt::fmt-header-only
|
||||||
webgpu
|
webgpu
|
||||||
|
efsw-static
|
||||||
)
|
)
|
||||||
target_link_directories(${PROJECT_NAME} PUBLIC ${Stb_INCLUDE_DIR})
|
target_link_directories(${PROJECT_NAME} PUBLIC ${Stb_INCLUDE_DIR})
|
||||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VULKAN_HPP_NO_EXCEPTIONS VMA_NOT_NULL= VMA_NULLABLE=)
|
target_compile_definitions(${PROJECT_NAME} PUBLIC VULKAN_HPP_NO_EXCEPTIONS VMA_NOT_NULL= VMA_NULLABLE=)
|
||||||
|
|||||||
90
src/render/shader/render_pipeline.cpp
Normal file
90
src/render/shader/render_pipeline.cpp
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
#include "render_pipeline.h"
|
||||||
|
|
||||||
|
#include "shader_module.h"
|
||||||
|
#include "core/logger.h"
|
||||||
|
|
||||||
|
namespace mirai {
|
||||||
|
render_pipeline::render_pipeline(const std::shared_ptr<shader_module>& shader_module, wgpu::Device device) {
|
||||||
|
shader_module_ = shader_module;
|
||||||
|
device_ = device;
|
||||||
|
}
|
||||||
|
|
||||||
|
render_pipeline::~render_pipeline() {
|
||||||
|
if (render_pipeline_) {
|
||||||
|
render_pipeline_.release();
|
||||||
|
}
|
||||||
|
if (pipeline_layout_) {
|
||||||
|
pipeline_layout_.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool render_pipeline::begin_rebuild_pipeline(wgpu::Device device) {
|
||||||
|
render_pipeline_descriptor_.setDefault();
|
||||||
|
|
||||||
|
if (!pipeline_layout_) {
|
||||||
|
wgpu::PipelineLayoutDescriptor pipeline_layout_descriptor{};
|
||||||
|
pipeline_layout_descriptor.setDefault();
|
||||||
|
pipeline_layout_descriptor.label = "Render pipeline layout";
|
||||||
|
pipeline_layout_ = device.createPipelineLayout(pipeline_layout_descriptor);
|
||||||
|
if (!pipeline_layout_) {
|
||||||
|
MIRAI_LOG_ERROR("无法创建渲染管线布局");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render_pipeline_descriptor_.label = "Render pipeline";
|
||||||
|
render_pipeline_descriptor_.layout = pipeline_layout_;
|
||||||
|
|
||||||
|
wgpu::PrimitiveState primitive_state{};
|
||||||
|
primitive_state.setDefault();
|
||||||
|
primitive_state.topology = wgpu::PrimitiveTopology::TriangleList;
|
||||||
|
primitive_state.frontFace = wgpu::FrontFace::CCW;
|
||||||
|
primitive_state.cullMode = wgpu::CullMode::None;
|
||||||
|
|
||||||
|
wgpu::MultisampleState multisample_state{};
|
||||||
|
multisample_state.setDefault();
|
||||||
|
multisample_state.count = 1;
|
||||||
|
multisample_state.mask = 0xFFFFFFFF;
|
||||||
|
multisample_state.alphaToCoverageEnabled = false;
|
||||||
|
|
||||||
|
render_pipeline_descriptor_.primitive = primitive_state;
|
||||||
|
render_pipeline_descriptor_.multisample = multisample_state;
|
||||||
|
|
||||||
|
shader_module_->modify_pipeline_descriptor(render_pipeline_descriptor_);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool render_pipeline::end_rebuild_pipeline(wgpu::Device device) {
|
||||||
|
if (render_pipeline_) {
|
||||||
|
render_pipeline_.release();
|
||||||
|
render_pipeline_ = nullptr;
|
||||||
|
}
|
||||||
|
render_pipeline_ = device.createRenderPipeline(render_pipeline_descriptor_);
|
||||||
|
if (!render_pipeline_) {
|
||||||
|
MIRAI_LOG_ERROR("无法创建渲染管线");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu::RenderPipeline render_pipeline::get_pipeline() {
|
||||||
|
process_hot_reload();
|
||||||
|
return render_pipeline_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_pipeline::init() {
|
||||||
|
shader_module_->reload_shader_module();
|
||||||
|
begin_rebuild_pipeline(device_);
|
||||||
|
if (modify_descriptor_)
|
||||||
|
modify_descriptor_(render_pipeline_descriptor_);
|
||||||
|
end_rebuild_pipeline(device_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_pipeline::process_hot_reload() {
|
||||||
|
if (shader_module_->is_need_reload()) {
|
||||||
|
MIRAI_LOG_INFO("渲染管线检测到着色器模块已修改,正在重新加载...");
|
||||||
|
init();
|
||||||
|
MIRAI_LOG_INFO("渲染管线重新加载完成");
|
||||||
|
shader_module_->process_reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/render/shader/render_pipeline.h
Normal file
35
src/render/shader/render_pipeline.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <webgpu/webgpu.hpp>
|
||||||
|
|
||||||
|
#include "core/object.h"
|
||||||
|
|
||||||
|
namespace mirai {
|
||||||
|
class shader_module;
|
||||||
|
|
||||||
|
class render_pipeline : public object {
|
||||||
|
MIRAI_OBJECT_TYPE_INFO(render_pipeline, object)
|
||||||
|
public:
|
||||||
|
render_pipeline(const std::shared_ptr<shader_module>& shader_module, wgpu::Device device);
|
||||||
|
~render_pipeline() override;
|
||||||
|
|
||||||
|
void set_modify_descriptor_callback(std::function<void(wgpu::RenderPipelineDescriptor&)> callback) {
|
||||||
|
modify_descriptor_ = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] wgpu::RenderPipeline get_pipeline();
|
||||||
|
|
||||||
|
void init();
|
||||||
|
void process_hot_reload();
|
||||||
|
private:
|
||||||
|
bool begin_rebuild_pipeline(wgpu::Device device);
|
||||||
|
bool end_rebuild_pipeline(wgpu::Device device);
|
||||||
|
|
||||||
|
wgpu::Device device_;
|
||||||
|
wgpu::RenderPipeline render_pipeline_;
|
||||||
|
wgpu::PipelineLayout pipeline_layout_;
|
||||||
|
|
||||||
|
wgpu::RenderPipelineDescriptor render_pipeline_descriptor_;
|
||||||
|
std::shared_ptr<shader_module> shader_module_;
|
||||||
|
std::function<void(wgpu::RenderPipelineDescriptor&)> modify_descriptor_;
|
||||||
|
};
|
||||||
|
}
|
||||||
35
src/render/shader/shader_code.cpp
Normal file
35
src/render/shader/shader_code.cpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#include "shader_code.h"
|
||||||
|
|
||||||
|
#include "core/logger.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace mirai {
|
||||||
|
static efsw::FileWatcher file_watcher;
|
||||||
|
shader_file_code::shader_file_code(std::filesystem::path file_path): file_path_(std::move(file_path)) {
|
||||||
|
// 监听文件是否被修改
|
||||||
|
file_watcher.addWatch(file_path_.parent_path().string(), this, false);
|
||||||
|
file_watcher.watch();
|
||||||
|
}
|
||||||
|
|
||||||
|
shader_file_code::~shader_file_code() {
|
||||||
|
file_watcher.removeWatch(file_path_.parent_path().string());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string shader_file_code::get_code() const {
|
||||||
|
std::ifstream shader_file(file_path_, std::ios::in);
|
||||||
|
if (!shader_file.is_open()) {
|
||||||
|
MIRAI_LOG_ERROR("无法打开着色器文件: {}", file_path_.string());
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
std::string shader_code((std::istreambuf_iterator<char>(shader_file)), std::istreambuf_iterator<char>());
|
||||||
|
return shader_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
void shader_file_code::handleFileAction(efsw::WatchID watchid, const std::string& dir, const std::string& filename,
|
||||||
|
efsw::Action action, std::string oldFilename) {
|
||||||
|
// 先更新路径
|
||||||
|
file_path_ = std::filesystem::path(dir) / filename;
|
||||||
|
mark_dirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
71
src/render/shader/shader_code.h
Normal file
71
src/render/shader/shader_code.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include "efsw/efsw.hpp"
|
||||||
|
#include "render/webgpu-raii.hpp"
|
||||||
|
#include "core/object.h"
|
||||||
|
|
||||||
|
namespace mirai {
|
||||||
|
class shader_code : public object {
|
||||||
|
MIRAI_OBJECT_TYPE_INFO(shader_code, object)
|
||||||
|
public:
|
||||||
|
virtual ~shader_code() = default;
|
||||||
|
[[nodiscard]] virtual std::string get_code() const = 0;
|
||||||
|
|
||||||
|
virtual std::string get_name() const {
|
||||||
|
return "Unnamed Shader Code";
|
||||||
|
}
|
||||||
|
virtual bool is_need_reload() const { return is_dirty.load(); }
|
||||||
|
|
||||||
|
void process_reload() {
|
||||||
|
is_dirty = false;
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
void mark_dirty() {
|
||||||
|
is_dirty = true;
|
||||||
|
}
|
||||||
|
std::atomic_bool is_dirty{ false };
|
||||||
|
};
|
||||||
|
|
||||||
|
class shader_file_code : public shader_code, public efsw::FileWatchListener {
|
||||||
|
MIRAI_OBJECT_TYPE_INFO(shader_file_code, shader_code)
|
||||||
|
public:
|
||||||
|
explicit shader_file_code(std::filesystem::path file_path);
|
||||||
|
~shader_file_code() override;
|
||||||
|
|
||||||
|
[[nodiscard]] std::string get_code() const override;
|
||||||
|
std::string get_name() const override {
|
||||||
|
return file_path_.filename().string();
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
void handleFileAction(efsw::WatchID watchid, const std::string& dir, const std::string& filename, efsw::Action action, std::string oldFilename) override;
|
||||||
|
private:
|
||||||
|
std::filesystem::path file_path_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class shader_string_code : public shader_code {
|
||||||
|
MIRAI_OBJECT_TYPE_INFO(shader_string_code, shader_code)
|
||||||
|
public:
|
||||||
|
explicit shader_string_code(std::string code)
|
||||||
|
: code_(std::move(code)) {}
|
||||||
|
|
||||||
|
void set_code(const std::string& new_code) {
|
||||||
|
code_ = new_code;
|
||||||
|
mark_dirty();
|
||||||
|
}
|
||||||
|
[[nodiscard]] std::string get_code() const override {
|
||||||
|
return code_;
|
||||||
|
}
|
||||||
|
void set_name(const std::string& name) {
|
||||||
|
name_ = name;
|
||||||
|
}
|
||||||
|
std::string get_name() const override {
|
||||||
|
return name_.empty() ? "String Shader Code" : name_;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::string code_;
|
||||||
|
std::string name_;
|
||||||
|
};
|
||||||
|
}
|
||||||
69
src/render/shader/shader_entry_point.cpp
Normal file
69
src/render/shader/shader_entry_point.cpp
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#include "shader_entry_point.h"
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
#include "shader_module.h"
|
||||||
|
#include "core/logger.h"
|
||||||
|
|
||||||
|
namespace mirai {
|
||||||
|
bool shader_entry_point::auto_set_entry_point_name(std::string_view shader_code) {
|
||||||
|
if (auto entry_name_opt = get_entry_name(std::string(shader_code), get_stage_name())) {
|
||||||
|
set_entry_point_name(*entry_name_opt);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void shader_entry_point::modify_pipeline_descriptor(const std::shared_ptr<shader_module> sm,
|
||||||
|
wgpu::RenderPipelineDescriptor& descriptor) {
|
||||||
|
if (entry_point_name_.empty()) {
|
||||||
|
if (!auto_set_entry_point_name(sm->get_code())) {
|
||||||
|
MIRAI_LOG_WARN("无法为阶段 {} 自动设置入口点名称,请手动设置。", get_stage_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> shader_entry_point::get_entry_name(const std::string& code_str, std::string_view stage) const {
|
||||||
|
// 查找类似 "@vertex fn main_vs(" 的模式
|
||||||
|
std::regex entry_point_regex(
|
||||||
|
"@" + std::string(stage) + R"(\s+fn\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\()"
|
||||||
|
);
|
||||||
|
|
||||||
|
std::smatch match;
|
||||||
|
// 使用cbegin()和cend()来安全地搜索std::string
|
||||||
|
if (std::regex_search(code_str.cbegin(), code_str.cend(), match, entry_point_regex)) {
|
||||||
|
// match[0] 是整个匹配的字符串 (例如 "@vertex fn main_vs(")
|
||||||
|
// match[1] 是第一个捕获组 (我们想要的函数名 "main_vs")
|
||||||
|
if (match.size() >= 2) {
|
||||||
|
return match[1].str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void shader_entry_point_vertex::modify_pipeline_descriptor(std::shared_ptr<shader_module> sm,
|
||||||
|
wgpu::RenderPipelineDescriptor& descriptor) {
|
||||||
|
shader_entry_point::modify_pipeline_descriptor(sm, descriptor);
|
||||||
|
|
||||||
|
wgpu::VertexState vertex_state;
|
||||||
|
vertex_state.setDefault();
|
||||||
|
vertex_state.module = sm->get_shader_module();
|
||||||
|
vertex_state.entryPoint = entry_point_name_.c_str();
|
||||||
|
|
||||||
|
descriptor.vertex = vertex_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void shader_entry_point_fragment::modify_pipeline_descriptor(std::shared_ptr<shader_module> sm,
|
||||||
|
wgpu::RenderPipelineDescriptor& descriptor) {
|
||||||
|
shader_entry_point::modify_pipeline_descriptor(sm, descriptor);
|
||||||
|
fragment_state_.setDefault();
|
||||||
|
fragment_state_.module = sm->get_shader_module();
|
||||||
|
fragment_state_.entryPoint = entry_point_name_.c_str();
|
||||||
|
fragment_state_.targetCount = static_cast<uint32_t>(color_target_states_.size());
|
||||||
|
fragment_state_.targets = color_target_states_.data();
|
||||||
|
fragment_state_.constantCount = static_cast<uint32_t>(constant_entries_.size());
|
||||||
|
fragment_state_.constants = constant_entries_.data();
|
||||||
|
|
||||||
|
descriptor.fragment = &fragment_state_;
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/render/shader/shader_entry_point.h
Normal file
67
src/render/shader/shader_entry_point.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include <webgpu/webgpu.hpp>
|
||||||
|
|
||||||
|
#include "core/object.h"
|
||||||
|
|
||||||
|
namespace mirai {
|
||||||
|
class shader_module;
|
||||||
|
|
||||||
|
class shader_entry_point : public object {
|
||||||
|
MIRAI_OBJECT_TYPE_INFO(shader_entry_point, object)
|
||||||
|
public:
|
||||||
|
virtual ~shader_entry_point() = default;
|
||||||
|
virtual bool auto_set_entry_point_name(std::string_view shader_code);
|
||||||
|
|
||||||
|
[[nodiscard]] std::string_view get_entry_point_name() const {
|
||||||
|
return entry_point_name_;
|
||||||
|
}
|
||||||
|
void set_entry_point_name(std::string_view shader_module) {
|
||||||
|
entry_point_name_ = shader_module;
|
||||||
|
}
|
||||||
|
virtual void modify_pipeline_descriptor(std::shared_ptr<shader_module> sm, wgpu::RenderPipelineDescriptor& descriptor);
|
||||||
|
protected:
|
||||||
|
std::optional<std::string> get_entry_name(const std::string& code_str, std::string_view stage) const;
|
||||||
|
virtual std::string_view get_stage_name() const = 0;
|
||||||
|
std::string entry_point_name_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class shader_entry_point_vertex : public shader_entry_point {
|
||||||
|
MIRAI_OBJECT_TYPE_INFO(shader_entry_point_vertex, shader_entry_point)
|
||||||
|
public:
|
||||||
|
void modify_pipeline_descriptor(std::shared_ptr<shader_module> sm, wgpu::RenderPipelineDescriptor& descriptor) override;
|
||||||
|
protected:
|
||||||
|
std::string_view get_stage_name() const override {
|
||||||
|
return "vertex";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class shader_entry_point_fragment : public shader_entry_point {
|
||||||
|
MIRAI_OBJECT_TYPE_INFO(shader_entry_point_fragment, shader_entry_point)
|
||||||
|
public:
|
||||||
|
// 添加颜色目标状态 !注意不要存储这个对象, 因为这个对象地址随时会被改变!
|
||||||
|
auto& add_color_target_state() {
|
||||||
|
auto& out = color_target_states_.emplace_back();
|
||||||
|
out.setDefault();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
// 添加常量条目 !注意不要存储这个对象, 因为这个对象地址随时会被改变!
|
||||||
|
auto& add_constant_entry() {
|
||||||
|
auto& out = constant_entries_.emplace_back();
|
||||||
|
out.setDefault();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
void modify_pipeline_descriptor(std::shared_ptr<shader_module> sm, wgpu::RenderPipelineDescriptor& descriptor) override;
|
||||||
|
protected:
|
||||||
|
std::string_view get_stage_name() const override {
|
||||||
|
return "fragment";
|
||||||
|
}
|
||||||
|
wgpu::FragmentState fragment_state_;
|
||||||
|
std::vector<wgpu::ColorTargetState> color_target_states_;
|
||||||
|
std::vector<wgpu::ConstantEntry> constant_entries_;
|
||||||
|
};
|
||||||
|
}
|
||||||
5
src/render/shader/shader_manager.cpp
Normal file
5
src/render/shader/shader_manager.cpp
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#include "shader_manager.h"
|
||||||
|
|
||||||
|
namespace mirai {
|
||||||
|
|
||||||
|
}
|
||||||
7
src/render/shader/shader_manager.h
Normal file
7
src/render/shader/shader_manager.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace mirai {
|
||||||
|
class shader_manager {
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
59
src/render/shader/shader_module.cpp
Normal file
59
src/render/shader/shader_module.cpp
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#include "shader_module.h"
|
||||||
|
|
||||||
|
#include "shader_code.h"
|
||||||
|
#include "core/logger.h"
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
#include "shader_entry_point.h"
|
||||||
|
|
||||||
|
namespace mirai {
|
||||||
|
shader_module::shader_module(const wgpu::Device& device, std::shared_ptr<shader_code> code) {
|
||||||
|
if (!code) {
|
||||||
|
throw std::runtime_error("shader_module:代码为空");
|
||||||
|
}
|
||||||
|
device_ = device;
|
||||||
|
code_ = std::move(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
shader_module::~shader_module() {
|
||||||
|
if (shader_module_)
|
||||||
|
shader_module_.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void shader_module::modify_pipeline_descriptor(wgpu::RenderPipelineDescriptor& descriptor) {
|
||||||
|
const auto p = shared_this();
|
||||||
|
for (const auto& entry_point : entry_points_) {
|
||||||
|
entry_point->modify_pipeline_descriptor(p, descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void shader_module::reload_shader_module() {
|
||||||
|
MIRAI_LOG_INFO("着色器 {} 开始热重载", code_->get_name());
|
||||||
|
std::string shader_code = code_->get_code();
|
||||||
|
if (shader_code.empty()) {
|
||||||
|
MIRAI_LOG_WARN("着色器代码是空的,无法创建着色器模块。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (shader_module_) {
|
||||||
|
shader_module_.release();
|
||||||
|
shader_module_ = nullptr;
|
||||||
|
}
|
||||||
|
auto shader_name = "Shader Module: " + code_->get_name();
|
||||||
|
wgpu::ShaderModuleWGSLDescriptor wgsl_desc{};
|
||||||
|
wgsl_desc.setDefault();
|
||||||
|
wgsl_desc.code = shader_code.c_str();
|
||||||
|
|
||||||
|
wgpu::ShaderModuleDescriptor shader_module_desc{};
|
||||||
|
shader_module_desc.setDefault();
|
||||||
|
shader_module_desc.label = shader_name.c_str();
|
||||||
|
shader_module_desc.nextInChain = &wgsl_desc.chain;
|
||||||
|
|
||||||
|
shader_module_ = device_.createShaderModule(shader_module_desc);
|
||||||
|
if (!shader_module_) {
|
||||||
|
MIRAI_LOG_ERROR("无法创建着色器模块 {}", code_->get_name());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MIRAI_LOG_INFO("着色器 {} 热重载完成", code_->get_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/render/shader/shader_module.h
Normal file
35
src/render/shader/shader_module.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include "shader_code.h"
|
||||||
|
#include "core/object.h"
|
||||||
|
#include "render/webgpu-raii.hpp"
|
||||||
|
|
||||||
|
namespace mirai {
|
||||||
|
class shader_entry_point;
|
||||||
|
class shader_code;
|
||||||
|
|
||||||
|
class shader_module : public object {
|
||||||
|
MIRAI_OBJECT_TYPE_INFO(shader_module, object);
|
||||||
|
public:
|
||||||
|
shader_module(const wgpu::Device& device, std::shared_ptr<shader_code> code);
|
||||||
|
~shader_module() override;
|
||||||
|
|
||||||
|
void add_entry_point(const std::shared_ptr<shader_entry_point>& entry_point) {
|
||||||
|
entry_points_.push_back(entry_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
void modify_pipeline_descriptor(wgpu::RenderPipelineDescriptor& descriptor);
|
||||||
|
void reload_shader_module();
|
||||||
|
auto get_shader_module() const { return shader_module_; }
|
||||||
|
auto get_code() const { return code_->get_code(); }
|
||||||
|
auto is_need_reload() const { return code_->is_need_reload(); }
|
||||||
|
void process_reload() const { code_->process_reload(); }
|
||||||
|
private:
|
||||||
|
wgpu::Device device_;
|
||||||
|
wgpu::ShaderModule shader_module_;
|
||||||
|
std::vector<std::shared_ptr<shader_entry_point>> entry_points_;
|
||||||
|
std::shared_ptr<shader_code> code_;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -5,83 +5,29 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "core/object.h"
|
||||||
|
#include "shader/render_pipeline.h"
|
||||||
|
#include "shader/shader_code.h"
|
||||||
|
#include "shader/shader_entry_point.h"
|
||||||
|
#include "shader/shader_module.h"
|
||||||
|
|
||||||
namespace mirai {
|
namespace mirai {
|
||||||
void wgpu_context::test_shader() {
|
void wgpu_context::test_shader() {
|
||||||
std::filesystem::path shader_path = R"(F:\Projects\mirai\src\render\test_shader_code.wgsl)";
|
std::filesystem::path shader_path = R"(F:\Projects\mirai\src\render\test_shader_code.wgsl)";
|
||||||
std::ifstream shader_file(shader_path, std::ios::in);
|
auto code = make_obj<shader_file_code>(shader_path);
|
||||||
if (!shader_file.is_open()) {
|
auto sm = make_obj<shader_module>(wgpu_device_, code);
|
||||||
MIRAI_LOG_ERROR("无法打开测试着色器文件: {}", shader_path.string());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::string shader_code((std::istreambuf_iterator<char>(shader_file)), std::istreambuf_iterator<char>());
|
|
||||||
MIRAI_LOG_INFO("测试着色器代码:\n{}", shader_code);
|
|
||||||
|
|
||||||
wgpu::ShaderModuleWGSLDescriptor wgpu_shader_module_descriptor{};
|
auto vertex_entry = make_obj<shader_entry_point_vertex>();
|
||||||
wgpu_shader_module_descriptor.setDefault();
|
auto frga_entry = make_obj<shader_entry_point_fragment>();
|
||||||
wgpu_shader_module_descriptor.code = shader_code.c_str();
|
auto& color_target = frga_entry->add_color_target_state();
|
||||||
|
color_target.format = wgpu::TextureFormat::BGRA8Unorm;
|
||||||
|
color_target.writeMask = wgpu::ColorWriteMask::All;
|
||||||
|
|
||||||
wgpu::ShaderModuleDescriptor shader_module_descriptor{};
|
sm->add_entry_point(vertex_entry);
|
||||||
shader_module_descriptor.setDefault();
|
sm->add_entry_point(frga_entry);
|
||||||
shader_module_descriptor.label = "Test shader module";
|
|
||||||
shader_module_descriptor.nextInChain = &wgpu_shader_module_descriptor.chain;
|
|
||||||
|
|
||||||
wgpu::ShaderModule shader_module = get_device().createShaderModule(shader_module_descriptor);
|
render_pipeline_ = make_obj<render_pipeline>(sm, wgpu_device_);
|
||||||
if (!shader_module) {
|
render_pipeline_->init();
|
||||||
MIRAI_LOG_ERROR("无法创建测试着色器模块");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
MIRAI_LOG_INFO("测试着色器模块创建成功");
|
|
||||||
|
|
||||||
wgpu::ColorTargetState color_target_state{};
|
|
||||||
color_target_state.setDefault();
|
|
||||||
color_target_state.format = wgpu::TextureFormat::BGRA8Unorm;
|
|
||||||
color_target_state.writeMask = wgpu::ColorWriteMask::All;
|
|
||||||
|
|
||||||
wgpu::PrimitiveState primitive_state{};
|
|
||||||
primitive_state.setDefault();
|
|
||||||
primitive_state.topology = wgpu::PrimitiveTopology::TriangleList;
|
|
||||||
primitive_state.frontFace = wgpu::FrontFace::CCW;
|
|
||||||
primitive_state.cullMode = wgpu::CullMode::None;
|
|
||||||
|
|
||||||
wgpu::MultisampleState multisample_state{};
|
|
||||||
multisample_state.setDefault();
|
|
||||||
multisample_state.count = 1;
|
|
||||||
multisample_state.mask = 0xFFFFFFFF;
|
|
||||||
multisample_state.alphaToCoverageEnabled = false;
|
|
||||||
|
|
||||||
wgpu::VertexState vertex_state{};
|
|
||||||
vertex_state.setDefault();
|
|
||||||
vertex_state.module = shader_module;
|
|
||||||
vertex_state.entryPoint = "vs_main";
|
|
||||||
|
|
||||||
wgpu::FragmentState fragment_state{};
|
|
||||||
fragment_state.setDefault();
|
|
||||||
fragment_state.module = shader_module;
|
|
||||||
fragment_state.entryPoint = "fs_main";
|
|
||||||
fragment_state.targetCount = 1;
|
|
||||||
fragment_state.targets = &color_target_state;
|
|
||||||
|
|
||||||
wgpu::PipelineLayoutDescriptor pipeline_layout_descriptor{};
|
|
||||||
pipeline_layout_descriptor.setDefault();
|
|
||||||
pipeline_layout_descriptor.label = "Test pipeline layout";
|
|
||||||
wgpu::PipelineLayout pipeline_layout = get_device().createPipelineLayout(pipeline_layout_descriptor);
|
|
||||||
if (!pipeline_layout) {
|
|
||||||
MIRAI_LOG_ERROR("无法创建测试管线布局");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wgpu::RenderPipelineDescriptor render_pipeline_descriptor{};
|
|
||||||
render_pipeline_descriptor.setDefault();
|
|
||||||
render_pipeline_descriptor.label = "Test render pipeline";
|
|
||||||
render_pipeline_descriptor.layout = pipeline_layout;
|
|
||||||
render_pipeline_descriptor.vertex = vertex_state;
|
|
||||||
render_pipeline_descriptor.fragment = &fragment_state;
|
|
||||||
render_pipeline_descriptor.primitive = primitive_state;
|
|
||||||
render_pipeline_descriptor.multisample = multisample_state;
|
|
||||||
render_pipeline_ = get_device().createRenderPipeline(render_pipeline_descriptor);
|
|
||||||
if (!render_pipeline_) {
|
|
||||||
MIRAI_LOG_ERROR("无法创建测试渲染管线");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wgpu_context::setup() {
|
bool wgpu_context::setup() {
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
#include "render/webgpu-raii.hpp"
|
#include "render/webgpu-raii.hpp"
|
||||||
|
|
||||||
namespace mirai {
|
namespace mirai {
|
||||||
|
class render_pipeline;
|
||||||
|
|
||||||
class wgpu_context {
|
class wgpu_context {
|
||||||
public:
|
public:
|
||||||
static auto& instance() {
|
static auto& instance() {
|
||||||
@@ -30,6 +32,6 @@ namespace mirai {
|
|||||||
wgpu_context() = default;
|
wgpu_context() = default;
|
||||||
wgpu::Instance wgpu_instance_;
|
wgpu::Instance wgpu_instance_;
|
||||||
wgpu::Device wgpu_device_;
|
wgpu::Device wgpu_device_;
|
||||||
wgpu::RenderPipeline render_pipeline_;
|
std::shared_ptr<render_pipeline> render_pipeline_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "core/logger.h"
|
#include "core/logger.h"
|
||||||
#include "render/wgpu_context.h"
|
#include "render/wgpu_context.h"
|
||||||
|
#include "render/shader/render_pipeline.h"
|
||||||
|
|
||||||
namespace mirai {
|
namespace mirai {
|
||||||
void_result_t window::make_window(window_config config) {
|
void_result_t window::make_window(window_config config) {
|
||||||
@@ -65,7 +66,7 @@ namespace mirai {
|
|||||||
render_pass_descriptor.colorAttachments = &color_attachment;
|
render_pass_descriptor.colorAttachments = &color_attachment;
|
||||||
|
|
||||||
wgpu::raii::RenderPassEncoder render_pass = encoder->beginRenderPass(render_pass_descriptor);
|
wgpu::raii::RenderPassEncoder render_pass = encoder->beginRenderPass(render_pass_descriptor);
|
||||||
render_pass->setPipeline(pipeline);
|
render_pass->setPipeline(pipeline->get_pipeline());
|
||||||
render_pass->draw(3, 1, 0, 0);
|
render_pass->draw(3, 1, 0, 0);
|
||||||
render_pass->end();
|
render_pass->end();
|
||||||
|
|
||||||
|
|||||||
1
third_party/efsw
vendored
Submodule
1
third_party/efsw
vendored
Submodule
Submodule third_party/efsw added at 04d17474f1
Reference in New Issue
Block a user