Archived
1
Fork 0

Add basic shader editing system

POC, only registered shader is the sky shader
This commit is contained in:
redstrate 2021-02-16 17:10:37 -05:00
parent e8032b8cf2
commit e8fc757d99
18 changed files with 239 additions and 61 deletions

View file

@ -1,3 +1,10 @@
#pragma once
#include <string_view>
void draw_debug_ui();
void load_debug_options();
void save_debug_options();
std::string_view get_shader_source_directory();

View file

@ -11,6 +11,13 @@
#include "imgui_utility.hpp"
#include "scene.hpp"
#include "renderer.hpp"
#include "file.hpp"
struct Options {
std::string shader_source_path;
};
static inline Options options;
void draw_general() {
ImGui::Text("Platform: %s", platform::get_name());
@ -145,6 +152,51 @@ void draw_renderer() {
}
}
static inline std::string selected_shader;
static inline std::string loaded_shader_string;
void draw_shader_editor() {
if(options.shader_source_path.empty()) {
ImGui::Text("You haven't specified a shader source path yet. Please select one below:");
if(ImGui::Button("Select Path")) {
platform::open_dialog(false, [](std::string path) {
// open_dialog() can't select folders yet, so use this as a workaround
options.shader_source_path = file::Path(path).parent_path().string();
});
}
} else {
if(ImGui::BeginCombo("Select", nullptr)) {
for(auto& shader : engine->get_renderer()->registered_shaders) {
if(ImGui::Selectable(shader.filename.data())) {
selected_shader = shader.filename;
loaded_shader_string.clear();
}
}
ImGui::EndCombo();
}
if(!selected_shader.empty()) {
if(loaded_shader_string.empty()) {
file::Path base_shader_path = options.shader_source_path;
shader_compiler.set_include_path(base_shader_path.string());
file::Path shader_path = file::Path(selected_shader);
auto file = file::open(base_shader_path / shader_path.replace_extension(shader_path.extension().string() + ".glsl"));
loaded_shader_string = file->read_as_string();
}
ImGui::InputTextMultiline("Source", &loaded_shader_string);
if(ImGui::Button("Reload"))
engine->get_renderer()->reload_shader(selected_shader, loaded_shader_string);
}
}
}
void draw_debug_ui() {
if(ImGui::Begin("General"))
draw_general();
@ -165,4 +217,21 @@ void draw_debug_ui() {
draw_renderer();
ImGui::End();
if(ImGui::Begin("Shader Editor"))
draw_shader_editor();
ImGui::End();
}
void load_debug_options() {
// stub
}
void save_debug_options() {
// stub
}
std::string_view get_shader_source_directory() {
return options.shader_source_path;
}

View file

@ -443,17 +443,17 @@ GFXPipeline* GFXMetal::create_graphics_pipeline(const GFXGraphicsPipelineCreateI
MTLRenderPipelineDescriptor *pipelineDescriptor = [MTLRenderPipelineDescriptor new];
const bool has_vertex_stage = !info.shaders.vertex_path.empty() || !info.shaders.vertex_src.empty();
const bool has_fragment_stage = !info.shaders.fragment_path.empty() || !info.shaders.fragment_src.empty();
const bool has_vertex_stage = !info.shaders.vertex_src.empty();
const bool has_fragment_stage = !info.shaders.fragment_src.empty();
if(has_vertex_stage) {
id<MTLLibrary> vertexLibrary;
{
std::string vertex_src;
if(info.shaders.vertex_path.empty()) {
if(info.shaders.vertex_src.is_string()) {
vertex_src = info.shaders.vertex_src.as_string();
} else {
const auto vertex_path = utility::format("{}.msl", info.shaders.vertex_path);
const auto vertex_path = utility::format("{}.msl", info.shaders.vertex_src.as_path().string());
auto file = file::open(file::internal_domain / vertex_path);
if(file != std::nullopt) {
@ -471,8 +471,8 @@ GFXPipeline* GFXMetal::create_graphics_pipeline(const GFXGraphicsPipelineCreateI
id<MTLFunction> vertexFunc = [vertexLibrary newFunctionWithName:@"main0" constantValues:vertex_constants error:nil];
if(debug_enabled)
vertexFunc.label = [NSString stringWithFormat:@"%s", info.shaders.vertex_path.data()];
if(debug_enabled && info.shaders.vertex_src.is_path())
vertexFunc.label = [NSString stringWithFormat:@"%s", info.shaders.vertex_src.as_path().string().data()];
pipelineDescriptor.vertexFunction = vertexFunc;
}
@ -482,10 +482,10 @@ GFXPipeline* GFXMetal::create_graphics_pipeline(const GFXGraphicsPipelineCreateI
id<MTLLibrary> fragmentLibrary;
{
std::string fragment_src;
if(info.shaders.fragment_path.empty()) {
if(info.shaders.fragment_src.is_string()) {
fragment_src = info.shaders.fragment_src.as_string();
} else {
const auto fragment_path = utility::format("{}.msl", info.shaders.fragment_path);
const auto fragment_path = utility::format("{}.msl", info.shaders.fragment_src.as_path().string());
auto file = file::open(file::internal_domain / fragment_path);
if(file != std::nullopt) {
@ -504,8 +504,8 @@ GFXPipeline* GFXMetal::create_graphics_pipeline(const GFXGraphicsPipelineCreateI
id<MTLFunction> fragmentFunc = [fragmentLibrary newFunctionWithName:@"main0" constantValues:fragment_constants error:nil];
if(debug_enabled)
fragmentFunc.label = [NSString stringWithFormat:@"%s", info.shaders.fragment_path.data()];
if(debug_enabled && info.shaders.fragment_src.is_path())
fragmentFunc.label = [NSString stringWithFormat:@"%s", info.shaders.fragment_src.as_path().string().data()];
pipelineDescriptor.fragmentFunction = fragmentFunc;
}

View file

@ -157,8 +157,6 @@ struct GFXGraphicsPipelineCreateInfo {
std::string label; // only used for debug
struct Shaders {
std::string_view vertex_path, fragment_path;
ShaderSource vertex_src, fragment_src;
GFXShaderConstants vertex_constants, fragment_constants;

3
engine/platform/CMakeLists.txt Executable file
View file

@ -0,0 +1,3 @@
add_library(Platform INTERFACE)
target_include_directories(Platform INTERFACE include)
target_link_libraries(Platform INTERFACE Utility)

View file

@ -10,6 +10,7 @@
#include "common.hpp"
#include "render_options.hpp"
#include "path.hpp"
#include "shadercompiler.hpp"
namespace ui {
class Screen;
@ -129,6 +130,20 @@ public:
GFXPipeline* renderToUnormTexturePipeline = nullptr;
GFXRenderPass* viewportRenderPass = nullptr;
ShaderSource register_shader(const std::string_view shader_file);
void associate_shader_reload(const std::string_view shader_file, const std::function<void()> reload_function);
void reload_shader(const std::string_view shader_file, const std::string_view shader_source);
struct RegisteredShader {
std::string filename;
std::string injected_shader_source;
std::function<void()> reload_function;
};
std::vector<RegisteredShader> registered_shaders;
bool reloading_shader = false;
private:
void createDummyTexture();
void createOffscreenResources();

View file

@ -27,9 +27,9 @@ DoFPass::DoFPass(GFX* gfx, Renderer* renderer) : renderer(renderer) {
height_constant.value = extent.height;
GFXGraphicsPipelineCreateInfo create_info = {};
create_info.shaders.vertex_path = "dof.vert";
create_info.shaders.vertex_src = file::Path("dof.vert");
create_info.shaders.vertex_constants = {width_constant, height_constant};
create_info.shaders.fragment_path = "dof.frag";
create_info.shaders.fragment_src = file::Path("dof.frag");
create_info.shader_input.bindings = {
{0, GFXBindingType::StorageImage},

View file

@ -14,8 +14,8 @@ GaussianHelper::GaussianHelper(GFX* gfx, const prism::Extent extent) : extent(ex
// pipeline
GFXGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.label = "Gaussian";
pipelineInfo.shaders.vertex_path = "gaussian.vert";
pipelineInfo.shaders.fragment_path = "gaussian.frag";
pipelineInfo.shaders.vertex_src = file::Path("gaussian.vert");
pipelineInfo.shaders.fragment_src = file::Path("gaussian.frag");
pipelineInfo.rasterization.primitive_type = GFXPrimitiveType::TriangleStrip;

View file

@ -22,8 +22,8 @@ void ImGuiPass::initialize() {
void ImGuiPass::resize(const prism::Extent extent) {
GFXGraphicsPipelineCreateInfo createInfo;
createInfo.label = "ImGui";
createInfo.shaders.vertex_path = "imgui.vert";
createInfo.shaders.fragment_path = "imgui.frag";
createInfo.shaders.vertex_src = file::Path("imgui.vert");
createInfo.shaders.fragment_src = file::Path("imgui.frag");
GFXVertexInput vertexInput = {};
vertexInput.stride = sizeof(ImDrawVert);

View file

@ -36,7 +36,7 @@ ShaderSource get_shader(std::string filename, bool skinned, bool cubemap) {
GFXPipeline* MaterialCompiler::create_static_pipeline(GFXGraphicsPipelineCreateInfo createInfo, bool positions_only, bool cubemap) {
// take vertex src
std::string vertex_path = createInfo.shaders.vertex_path.data();
std::string vertex_path = createInfo.shaders.vertex_src.as_path().string();
vertex_path += ".glsl";
if (positions_only)
@ -46,7 +46,6 @@ GFXPipeline* MaterialCompiler::create_static_pipeline(GFXGraphicsPipelineCreateI
createInfo.label += "cubemap ver";
createInfo.shaders.vertex_src = get_shader(vertex_path, false, cubemap);
createInfo.shaders.vertex_path = "";
if(positions_only) {
createInfo.vertex_input.inputs = {
@ -81,11 +80,10 @@ GFXPipeline* MaterialCompiler::create_skinned_pipeline(GFXGraphicsPipelineCreate
createInfo.label += " (Skinned)";
// take vertex src
std::string vertex_path = createInfo.shaders.vertex_path.data();
std::string vertex_path = createInfo.shaders.vertex_src.as_path().string();
vertex_path += ".glsl";
createInfo.shaders.vertex_src = get_shader(vertex_path, true, false);
createInfo.shaders.vertex_path = "";
createInfo.shader_input.bindings.push_back({ 14, GFXBindingType::StorageBuffer });

View file

@ -24,6 +24,7 @@
#include "frustum.hpp"
#include "shadercompiler.hpp"
#include "asset.hpp"
#include "debug.hpp"
struct ElementInstance {
Vector4 color;
@ -710,8 +711,8 @@ void Renderer::create_mesh_pipeline(Material& material) {
GFXGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.label = "Mesh";
pipelineInfo.shaders.vertex_path = "mesh.vert";
pipelineInfo.shaders.fragment_path = "mesh.frag";
pipelineInfo.shaders.vertex_src = file::Path("mesh.vert");
pipelineInfo.shaders.fragment_src = file::Path("mesh.frag");
pipelineInfo.shaders.vertex_constants = {materials_constant, lights_constant, spot_lights_constant, probes_constant};
pipelineInfo.shaders.fragment_constants = {materials_constant, lights_constant, spot_lights_constant, probes_constant};
@ -738,7 +739,6 @@ void Renderer::create_mesh_pipeline(Material& material) {
pipelineInfo.blending.dst_rgb = GFXBlendFactor::OneMinusSrcAlpha;
pipelineInfo.shaders.fragment_src = material_compiler.compile_material_fragment(material);
pipelineInfo.shaders.fragment_path = "";
for (auto [index, texture] : material.bound_textures) {
GFXShaderBinding binding;
@ -852,8 +852,8 @@ void Renderer::createPostPipeline() {
GFXGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.label = "Post";
pipelineInfo.shaders.vertex_path = "post.vert";
pipelineInfo.shaders.fragment_path = "post.frag";
pipelineInfo.shaders.vertex_src = file::Path("post.vert");
pipelineInfo.shaders.fragment_src = file::Path("post.frag");
pipelineInfo.shader_input.bindings = {
{4, GFXBindingType::PushConstant},
@ -904,8 +904,8 @@ void Renderer::createFontPipeline() {
GFXGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.label = "Text";
pipelineInfo.shaders.vertex_path = "text.vert";
pipelineInfo.shaders.fragment_path = "text.frag";
pipelineInfo.shaders.vertex_src = file::Path("text.vert");
pipelineInfo.shaders.fragment_src = file::Path("text.frag");
pipelineInfo.rasterization.primitive_type = GFXPrimitiveType::TriangleStrip;
@ -950,8 +950,8 @@ void Renderer::createSkyPipeline() {
pipelineInfo.label = "Sky";
pipelineInfo.render_pass = offscreenRenderPass;
pipelineInfo.shaders.vertex_path = "sky.vert";
pipelineInfo.shaders.fragment_path = "sky.frag";
pipelineInfo.shaders.vertex_src = register_shader("sky.vert");
pipelineInfo.shaders.fragment_src = register_shader("sky.frag");
pipelineInfo.shader_input.bindings = {
{1, GFXBindingType::PushConstant}
@ -964,14 +964,22 @@ void Renderer::createSkyPipeline() {
pipelineInfo.depth.depth_mode = GFXDepthMode::LessOrEqual;
skyPipeline = gfx->create_graphics_pipeline(pipelineInfo);
associate_shader_reload("sky.vert", [this] {
createSkyPipeline();
});
associate_shader_reload("sky.frag", [this] {
createSkyPipeline();
});
}
void Renderer::createUIPipeline() {
GFXGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.label = "UI";
pipelineInfo.shaders.vertex_path = "ui.vert";
pipelineInfo.shaders.fragment_path = "ui.frag";
pipelineInfo.shaders.vertex_src = file::Path("ui.vert");
pipelineInfo.shaders.fragment_src = file::Path("ui.frag");
pipelineInfo.rasterization.primitive_type = GFXPrimitiveType::TriangleStrip;
@ -1026,8 +1034,8 @@ void Renderer::createBRDF() {
GFXGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.label = "BRDF";
pipelineInfo.shaders.vertex_path = "brdf.vert";
pipelineInfo.shaders.fragment_path = "brdf.frag";
pipelineInfo.shaders.vertex_src = file::Path("brdf.vert");
pipelineInfo.shaders.fragment_src = file::Path("brdf.frag");
pipelineInfo.render_pass = brdfRenderPass;
@ -1100,3 +1108,64 @@ void Renderer::create_histogram_resources() {
average_luminance_texture = gfx->create_texture(texture_info);
}
ShaderSource Renderer::register_shader(const std::string_view shader_file) {
if(!reloading_shader) {
RegisteredShader shader;
shader.filename = shader_file;
registered_shaders.push_back(shader);
}
std::string found_shader_source;
for(auto& shader : registered_shaders) {
if(shader.filename == shader_file) {
found_shader_source = shader.injected_shader_source;
}
}
file::Path base_shader_path = get_shader_source_directory();
// if shader editor system is not initialized, use prebuilt shaders
if(base_shader_path.empty())
return file::Path(shader_file);
shader_compiler.set_include_path(base_shader_path.string());
file::Path shader_path = file::Path(shader_file);
ShaderStage stage;
if(shader_path.extension() == ".vert")
stage = ShaderStage::Vertex;
else if(shader_path.extension() == ".frag")
stage = ShaderStage::Fragment;
if(found_shader_source.empty()) {
auto file = file::open(base_shader_path / shader_path.replace_extension(shader_path.extension().string() + ".glsl"));
return shader_compiler.compile(ShaderLanguage::GLSL, stage, file->read_as_string(), ShaderLanguage::MSL).value();
} else {
return shader_compiler.compile(ShaderLanguage::GLSL, stage, found_shader_source, ShaderLanguage::MSL).value();
}
}
void Renderer::associate_shader_reload(const std::string_view shader_file, const std::function<void()> reload_function) {
if(reloading_shader)
return;
for(auto& shader : registered_shaders) {
if(shader.filename == shader_file)
shader.reload_function = reload_function;
}
}
void Renderer::reload_shader(const std::string_view shader_file, const std::string_view shader_source) {
for(auto& shader : registered_shaders) {
if(shader.filename == shader_file) {
shader.injected_shader_source = shader_source;
reloading_shader = true;
shader.reload_function();
reloading_shader = false;
}
}
}

View file

@ -399,8 +399,8 @@ void SceneCapture::createSkyResources() {
pipelineInfo.label = "Sky Scene Capture";
pipelineInfo.render_pass = renderPass;
pipelineInfo.shaders.vertex_path = "sky.vert";
pipelineInfo.shaders.fragment_path = "sky.frag";
pipelineInfo.shaders.vertex_src = file::Path("sky.vert");
pipelineInfo.shaders.fragment_src = file::Path("sky.frag");
pipelineInfo.shader_input.bindings = {
{1, GFXBindingType::PushConstant}
@ -443,8 +443,8 @@ void SceneCapture::createIrradianceResources() {
GFXGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.label = "Irradiance Convolution";
pipelineInfo.shaders.vertex_path = "irradiance.vert";
pipelineInfo.shaders.fragment_path = "irradiance.frag";
pipelineInfo.shaders.vertex_src = file::Path("irradiance.vert");
pipelineInfo.shaders.fragment_src = file::Path("irradiance.frag");
GFXVertexInput input;
input.stride = sizeof(Vector3);
@ -495,8 +495,8 @@ void SceneCapture::createPrefilterResources() {
GFXGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.label = "Prefilter";
pipelineInfo.shaders.vertex_path = "filter.vert";
pipelineInfo.shaders.fragment_path = "filter.frag";
pipelineInfo.shaders.vertex_src = file::Path("filter.vert");
pipelineInfo.shaders.fragment_src = file::Path("filter.frag");
pipelineInfo.shaders.fragment_constants = {size_constant};

View file

@ -315,7 +315,7 @@ void ShadowPass::create_pipelines() {
GFXGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.shaders.vertex_constants = {point_light_max_constant};
pipelineInfo.shaders.vertex_path = "shadow.vert";
pipelineInfo.shaders.vertex_src = file::Path("shadow.vert");
pipelineInfo.shaders.fragment_constants = { point_light_max_constant };
//pipelineInfo.shaders.fragment_path = "shadow.frag";
@ -360,7 +360,7 @@ void ShadowPass::create_pipelines() {
{
pipelineInfo.label = "Point Shadow";
pipelineInfo.shaders.fragment_path = "omnishadow.frag";
pipelineInfo.shaders.fragment_src = file::Path("omnishadow.frag");
auto [static_pipeline, skinned_pipeline] = material_compiler.create_pipeline_permutations(pipelineInfo, true);

View file

@ -112,8 +112,8 @@ void SMAAPass::create_pipelines() {
GFXGraphicsPipelineCreateInfo createInfo = {};
createInfo.label = "SMAA Edge";
createInfo.shaders.vertex_path = "edge.vert";
createInfo.shaders.fragment_path = "edge.frag";
createInfo.shaders.vertex_src = file::Path("edge.vert");
createInfo.shaders.fragment_src = file::Path("edge.frag");
createInfo.render_pass = render_pass;
@ -130,8 +130,8 @@ void SMAAPass::create_pipelines() {
edge_pipeline = gfx->create_graphics_pipeline(createInfo);
createInfo.label = "SMAA Blend";
createInfo.shaders.vertex_path = "blend.vert";
createInfo.shaders.fragment_path = "blend.frag";
createInfo.shaders.vertex_src = file::Path("blend.vert");
createInfo.shaders.fragment_src = file::Path("blend.frag");
createInfo.shader_input.bindings.push_back({3, GFXBindingType::Texture});
blend_pipeline = gfx->create_graphics_pipeline(createInfo);

View file

@ -7,6 +7,8 @@ set(SRC
add_library(ShaderCompiler STATIC ${SRC})
target_link_libraries(ShaderCompiler
PUBLIC
Platform
PRIVATE
Utility
Log

View file

@ -6,6 +6,8 @@
#include <string_view>
#include <optional>
#include "file.hpp"
/// The shader stage that the shader is written in.
enum class ShaderStage {
Vertex,
@ -41,8 +43,14 @@ public:
ShaderSource(const ShaderSource& rhs) : source (rhs.source) {}
ShaderSource(const std::string source_string) : source(source_string) {}
ShaderSource(const std::vector<uint32_t> source_bytecode) : source(source_bytecode) {}
ShaderSource(const file::Path shader_path) : source(shader_path) {}
std::variant<std::monostate, std::string, std::vector<uint32_t>> source;
std::variant<std::monostate, file::Path, std::string, std::vector<uint32_t>> source;
/// Returns a view of the shader source as a path.
file::Path as_path() const {
return std::get<file::Path>(source);
}
/// Returns a view of the shader source as plaintext.
std::string_view as_string() const {
@ -57,6 +65,14 @@ public:
bool empty() const {
return std::holds_alternative<std::monostate>(source);
}
bool is_path() const {
return std::holds_alternative<file::Path>(source);
}
bool is_string() const {
return std::holds_alternative<std::string>(source);
}
};
/// Compiles GLSL shaders to a specified shader language offline or at runtime.

View file

@ -9,14 +9,14 @@
#include "includer.hpp"
#include "defaultresources.hpp"
static inline std::string include_path;
static inline std::vector<std::string> include_path;
ShaderCompiler::ShaderCompiler() {
glslang::InitializeProcess();
}
void ShaderCompiler::set_include_path(const std::string_view path) {
include_path = path;
include_path.push_back(path.data());
}
const std::vector<uint32_t> compile_glsl_to_spv(const std::string_view source_string, const EShLanguage shader_language, const CompileOptions& options) {
@ -50,7 +50,8 @@ const std::vector<uint32_t> compile_glsl_to_spv(const std::string_view source_st
EShMessages messages = (EShMessages) (EShMsgDefault);
DirStackFileIncluder includer;
includer.pushExternalLocalDirectory(include_path);
for(auto path : include_path)
includer.pushExternalLocalDirectory(path);
if (!Shader.parse(&Resources, 100, false, messages, includer)) {
console::error(System::Renderer, "{}", Shader.getInfoLog());

View file

@ -21,8 +21,8 @@ void DebugPass::initialize() {
{
GFXGraphicsPipelineCreateInfo createInfo;
createInfo.shaders.vertex_path = "debug.vert";
createInfo.shaders.fragment_path = "debug.frag";
createInfo.shaders.vertex_src = file::Path("debug.vert");
createInfo.shaders.fragment_src = file::Path("debug.frag");
GFXVertexInput vertexInput = {};
vertexInput.stride = sizeof(Vector3);
@ -69,8 +69,8 @@ void DebugPass::initialize() {
// pipeline
GFXGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.shaders.vertex_path = "color.vert";
pipelineInfo.shaders.fragment_path = "color.frag";
pipelineInfo.shaders.vertex_src = file::Path("color.vert");
pipelineInfo.shaders.fragment_src = file::Path("color.frag");
GFXVertexInput input;
input.stride = sizeof(Vector3);
@ -110,8 +110,8 @@ void DebugPass::initialize() {
GFXGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.label = "Sobel";
pipelineInfo.shaders.vertex_path = "color.vert";
pipelineInfo.shaders.fragment_path = "color.frag";
pipelineInfo.shaders.vertex_src = file::Path("color.vert");
pipelineInfo.shaders.fragment_src = file::Path("color.frag");
GFXVertexInput input;
input.stride = sizeof(Vector3);
@ -142,8 +142,8 @@ void DebugPass::initialize() {
GFXGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.label = "Billboard";
pipelineInfo.shaders.vertex_path = "billboard.vert";
pipelineInfo.shaders.fragment_path = "billboard.frag";
pipelineInfo.shaders.vertex_src = file::Path("billboard.vert");
pipelineInfo.shaders.fragment_src = file::Path("billboard.frag");
pipelineInfo.shader_input.bindings = {
{1, GFXBindingType::PushConstant},