Added live reloading of shaders

master
Martin Felis 2016-12-17 23:04:50 +01:00
parent 273a8e010f
commit e88ea62139
3 changed files with 228 additions and 54 deletions

View File

@ -83,6 +83,7 @@ static void module_reload(struct module_state *state) {
Camera* camera = &gRenderer->cameras[gRenderer->activeCameraIndex];
assert (camera != nullptr);
SerializeBool (*gReadSerializer, "protot.RenderModule.draw_floor", gRenderer->drawFloor);
SerializeBool (*gReadSerializer, "protot.RenderModule.debug_enabled", gRenderer->drawDebug);
SerializeVec3 (*gReadSerializer, "protot.RenderModule.camera.eye", camera->eye);
SerializeVec3 (*gReadSerializer, "protot.RenderModule.camera.poi", camera->poi);
@ -95,6 +96,7 @@ static void module_unload(struct module_state *state) {
//(*gSerializer)["protot"]["RenderModule"]["active_camera"] = (double)gRenderer->activeCameraIndex;
SerializeBool (*gWriteSerializer, "protot.RenderModule.draw_floor", gRenderer->drawFloor);
SerializeBool (*gWriteSerializer, "protot.RenderModule.debug_enabled", gRenderer->drawDebug);
SerializeVec3 (*gWriteSerializer, "protot.RenderModule.camera.eye", camera->eye);
SerializeVec3 (*gWriteSerializer, "protot.RenderModule.camera.poi", camera->poi);
@ -114,6 +116,7 @@ static bool module_step(struct module_state *state, float dt) {
int width, height;
assert (gWindow != nullptr);
glfwGetWindowSize(gWindow, &width, &height);
state->renderer->updateShaders();
state->renderer->resize (width, height);
state->renderer->paintGL();
@ -138,11 +141,15 @@ bgfx::IndexBufferHandle cube_ibh;
bgfx::IndexBufferHandle cube_edges_ibh;
bgfx::VertexBufferHandle plane_vbh;
bgfx::IndexBufferHandle plane_ibh;
bgfx::DynamicVertexBufferHandle path_lines_vbh;
bgfx::DynamicIndexBufferHandle path_lines_ibh;
bgfx::DynamicVertexBufferHandle debug_lines_vbh;
bgfx::DynamicIndexBufferHandle debug_lines_ibh;
bgfx::UniformHandle u_time;
bgfx::UniformHandle u_color;
bgfx::UniformHandle u_lineparams;
bgfx::UniformHandle u_mtx;
bgfx::UniformHandle u_exposure;
@ -295,6 +302,78 @@ uint32_t packF4u(float _x, float _y = 0.0f, float _z = 0.0f, float _w = 0.0f)
return packUint32(xx, yy, zz, ww);
}
//
// Render programs
//
bool RenderProgram::checkModified() const {
// early out if we don't have filenames
if (vertexShaderFileName == ""
|| fragmentShaderFileName == "") {
return false;
}
struct stat attr;
bool stat_result = false;
// check mtime of vertex shader
stat_result = stat(vertexShaderFileName.c_str(), &attr);
if (stat_result == 0
&& attr.st_mtime != vertexShaderFileModTime) {
return true;
}
// check mtime of fragment shader
stat_result = stat(fragmentShaderFileName.c_str(), &attr);
if (stat_result == 0
&& attr.st_mtime != fragmentShaderFileModTime) {
return true;
}
return false;
}
bool RenderProgram::reload() {
int vertex_mtime = -1;
int fragment_mtime = -1;
struct stat attr;
bool stat_result = false;
// get mtime of vertex shader
stat_result = stat(vertexShaderFileName.c_str(), &attr);
if (stat_result == 0)
vertex_mtime = attr.st_mtime;
stat_result = stat(fragmentShaderFileName.c_str(), &attr);
if (stat_result == 0)
fragment_mtime = attr.st_mtime;
// we need to update the mod times, otherwise we keep reloading
// the same faulty files again and again.
vertexShaderFileModTime = vertex_mtime;
fragmentShaderFileModTime = fragment_mtime;
bgfx::ProgramHandle new_handle =
bgfxutils::loadProgramFromFiles (
vertexShaderFileName.c_str(),
fragmentShaderFileName.c_str()
);
if (bgfx::isValid(new_handle)) {
if (bgfx::isValid(program)) {
bgfx::destroyProgram(program);
}
cout << "Reload of shaders " << vertexShaderFileName << " and " << fragmentShaderFileName << " success!" << endl;
program = new_handle;
return true;
} else {
cout << "Reload of shaders " << vertexShaderFileName << " and " << fragmentShaderFileName << " failed!" << endl;
}
return false;
}
//
// Render states
//
@ -309,7 +388,7 @@ RenderState s_renderStates[RenderState::Count] = {
| BGFX_STATE_CULL_CCW
| BGFX_STATE_MSAA,
0,
bgfx::invalidHandle,
RenderProgram(),
RenderState::Skybox
},
{ // ShadowMap
@ -321,7 +400,7 @@ RenderState s_renderStates[RenderState::Count] = {
| BGFX_STATE_CULL_CCW
| BGFX_STATE_MSAA,
0,
bgfx::invalidHandle,
RenderProgram(),
RenderState::ShadowMap
},
{ // Scene
@ -333,7 +412,7 @@ RenderState s_renderStates[RenderState::Count] = {
| BGFX_STATE_CULL_CCW
| BGFX_STATE_MSAA,
0,
bgfx::invalidHandle,
RenderProgram(),
RenderState::Scene
},
{ // SceneTextured
@ -345,9 +424,23 @@ RenderState s_renderStates[RenderState::Count] = {
| BGFX_STATE_CULL_CCW
| BGFX_STATE_MSAA,
0,
bgfx::invalidHandle,
RenderProgram(),
RenderState::SceneTextured
},
{ // Lines
0
| BGFX_STATE_RGB_WRITE
| BGFX_STATE_ALPHA_WRITE
| BGFX_STATE_DEPTH_WRITE
| BGFX_STATE_DEPTH_TEST_LESS
// | BGFX_STATE_CULL_CCW
// | BGFX_STATE_PT_LINESTRIP
| BGFX_STATE_MSAA,
0,
RenderProgram(),
RenderState::Lines
},
{ // Debug
0
| BGFX_STATE_RGB_WRITE
@ -358,7 +451,7 @@ RenderState s_renderStates[RenderState::Count] = {
| BGFX_STATE_PT_LINES
| BGFX_STATE_MSAA,
0,
bgfx::invalidHandle,
RenderProgram(),
RenderState::Debug
}
};
@ -715,7 +808,7 @@ void Renderer::setupShaders() {
int grid_size = 1024;
int grid_border = 12;
uint8_t grid_color_border [4] = {255, 255, 255, 255};
uint8_t grid_color_border [4] = {32, 32, 32, 255};
// uint8_t grid_color_border [4] = {0, 0, 0, 0};
uint8_t grid_color_0[4] = {192, 192, 192, 255};
uint8_t grid_color_1[4] = {128, 128, 128, 255};
@ -748,6 +841,7 @@ void Renderer::setupShaders() {
u_time = bgfx::createUniform("u_time", bgfx::UniformType::Vec4);
u_color = bgfx::createUniform("u_color", bgfx::UniformType::Vec4);
u_lineparams = bgfx::createUniform("u_lineparams", bgfx::UniformType::Vec4);
m_timeOffset = bx::getHPCounter();
@ -774,7 +868,7 @@ void Renderer::setupShaders() {
memcpy(IBL::uniforms.m_lightDir, IBL::settings.m_lightDir, 3*sizeof(float) );
memcpy(IBL::uniforms.m_lightCol, IBL::settings.m_lightCol, 3*sizeof(float) );
s_renderStates[RenderState::Skybox].m_program = bgfxutils::loadProgramFromFiles("shaders/src/vs_ibl_skybox.sc", "shaders/src/fs_ibl_skybox.sc");
s_renderStates[RenderState::Skybox].m_program = RenderProgram("shaders/src/vs_ibl_skybox.sc", "shaders/src/fs_ibl_skybox.sc");
// Get renderer capabilities info.
const bgfx::Caps* caps = bgfx::getCaps();
@ -785,9 +879,9 @@ void Renderer::setupShaders() {
if (shadowSamplerSupported)
{
// Depth textures and shadow samplers are supported.
s_renderStates[RenderState::ShadowMap].m_program = bgfxutils::loadProgramFromFiles("shaders/src/vs_sms_mesh.sc", "shaders/src/fs_sms_shadow.sc");
s_renderStates[RenderState::Scene].m_program = bgfxutils::loadProgramFromFiles("shaders/src/vs_sms_mesh.sc", "shaders/src/fs_sms_mesh.sc");
s_renderStates[RenderState::SceneTextured].m_program = bgfxutils::loadProgramFromFiles("shaders/src/vs_sms_mesh_textured.sc", "shaders/src/fs_sms_mesh_textured.sc");
s_renderStates[RenderState::ShadowMap].m_program = RenderProgram("shaders/src/vs_sms_mesh.sc", "shaders/src/fs_sms_shadow.sc");
s_renderStates[RenderState::Scene].m_program = RenderProgram("shaders/src/vs_sms_mesh.sc", "shaders/src/fs_sms_mesh.sc");
s_renderStates[RenderState::SceneTextured].m_program = RenderProgram("shaders/src/vs_sms_mesh_textured.sc", "shaders/src/fs_sms_mesh_textured.sc");
lights[0].shadowMapTexture= bgfx::createTexture2D(lights[0].shadowMapSize, lights[0].shadowMapSize, false, 1, bgfx::TextureFormat::D16, BGFX_TEXTURE_COMPARE_LEQUAL);
bgfx::TextureHandle fbtextures[] = { lights[0].shadowMapTexture };
@ -797,9 +891,9 @@ void Renderer::setupShaders() {
{
// Depth textures and shadow samplers are not supported. Use float
// depth packing into color buffer instead.
s_renderStates[RenderState::ShadowMap].m_program = bgfxutils::loadProgram("vs_sms_shadow_pd", "fs_sms_shadow_pd");
s_renderStates[RenderState::Scene].m_program = bgfxutils::loadProgram("vs_sms_mesh", "fs_sms_mesh_pd");
s_renderStates[RenderState::SceneTextured].m_program = bgfxutils::loadProgram("vs_sms_mesh_textured", "fs_sms_mesh_pd_textured");
s_renderStates[RenderState::ShadowMap].m_program.program = bgfxutils::loadProgram("vs_sms_shadow_pd", "fs_sms_shadow_pd");
s_renderStates[RenderState::Scene].m_program.program = bgfxutils::loadProgram("vs_sms_mesh", "fs_sms_mesh_pd");
s_renderStates[RenderState::SceneTextured].m_program.program = bgfxutils::loadProgram("vs_sms_mesh_textured", "fs_sms_mesh_pd_textured");
lights[0].shadowMapTexture = bgfx::createTexture2D(lights[0].shadowMapSize, lights[0].shadowMapSize, false, 1, bgfx::TextureFormat::BGRA8, BGFX_TEXTURE_RT);
bgfx::TextureHandle fbtextures[] =
@ -810,7 +904,9 @@ void Renderer::setupShaders() {
lights[0].shadowMapFB = bgfx::createFrameBuffer(BX_COUNTOF(fbtextures), fbtextures, true);
}
s_renderStates[RenderState::Debug].m_program = bgfxutils::loadProgramFromFiles("shaders/src/vs_debug.sc", "shaders/src/fs_debug.sc");
s_renderStates[RenderState::Lines].m_program = RenderProgram("shaders/src/vs_lines.sc", "shaders/src/fs_lines.sc");
s_renderStates[RenderState::Debug].m_program = RenderProgram("shaders/src/vs_debug.sc", "shaders/src/fs_debug.sc");
}
void Renderer::setupRenderPasses() {
@ -841,6 +937,9 @@ void Renderer::setupRenderPasses() {
s_renderStates[RenderState::SceneTextured].m_textures[1].m_sampler = sceneDefaultTextureSampler;
s_renderStates[RenderState::SceneTextured].m_textures[1].m_texture = sceneDefaultTexture;
// Lines
s_renderStates[RenderState::Debug].m_viewId = RenderState::Lines;
// Debug
s_renderStates[RenderState::Debug].m_viewId = RenderState::Debug;
}
@ -968,6 +1067,9 @@ void Renderer::shutdown() {
bgfx::destroyDynamicVertexBuffer(debug_lines_vbh);
bgfx::destroyDynamicIndexBuffer(debug_lines_ibh);
bgfx::destroyDynamicVertexBuffer(path_lines_vbh);
bgfx::destroyDynamicIndexBuffer(path_lines_ibh);
bgfx::destroyIndexBuffer(cube_ibh);
bgfx::destroyIndexBuffer(cube_edges_ibh);
bgfx::destroyVertexBuffer(cube_vbh);
@ -985,10 +1087,11 @@ void Renderer::shutdown() {
bgfx::destroyUniform(u_time);
bgfx::destroyUniform(u_color);
bgfx::destroyUniform(u_lineparams);
for (uint8_t ii = 0; ii < RenderState::Count; ++ii) {
if (bgfx::isValid(s_renderStates[ii].m_program)) {
bgfx::destroyProgram(s_renderStates[ii].m_program);
if (bgfx::isValid(s_renderStates[ii].m_program.program)) {
bgfx::destroyProgram(s_renderStates[ii].m_program.program);
}
}
@ -1153,6 +1256,9 @@ void Renderer::paintGL() {
bgfx::setViewRect(RenderState::SceneTextured, 0, 0, width, height);
bgfx::setViewTransform(RenderState::SceneTextured, cameras[activeCameraIndex].mtxView, cameras[activeCameraIndex].mtxProj);
bgfx::setViewRect(RenderState::Lines, 0, 0, width, height);
bgfx::setViewTransform(RenderState::Lines, cameras[activeCameraIndex].mtxView, cameras[activeCameraIndex].mtxProj);
bgfx::setViewRect(RenderState::Debug, 0, 0, width, height);
bgfx::setViewTransform(RenderState::Debug, cameras[activeCameraIndex].mtxView, cameras[activeCameraIndex].mtxProj);
@ -1211,8 +1317,10 @@ void Renderer::paintGL() {
(float)cameras[activeCameraIndex].height, true);
IBL::uniforms.submit();
bgfx::submit(RenderState::Skybox, s_renderStates[RenderState::Skybox].m_program);
bgfx::submit(RenderState::Skybox, s_renderStates[RenderState::Skybox].m_program.program);
if (drawFloor)
{
// render the plane
uint32_t cached = bgfx::setTransform(mtxFloor);
for (uint32_t pass = 0; pass < RenderState::Count; ++pass) {
@ -1222,7 +1330,7 @@ void Renderer::paintGL() {
continue;
const RenderState& st = s_renderStates[pass];
if (!isValid(st.m_program)) {
if (!isValid(st.m_program.program)) {
continue;
}
@ -1243,7 +1351,8 @@ void Renderer::paintGL() {
bgfx::setIndexBuffer(plane_ibh);
bgfx::setVertexBuffer(plane_vbh);
bgfx::setState(st.m_state);
bgfx::submit(st.m_viewId, st.m_program);
bgfx::submit(st.m_viewId, st.m_program.program);
}
}
// render entities
@ -1283,7 +1392,7 @@ void Renderer::paintGL() {
bgfx::setIndexBuffer(cube_edges_ibh);
bgfx::setVertexBuffer(cube_vbh);
bgfx::setState(st.m_state);
bgfx::submit(st.m_viewId, st.m_program);
bgfx::submit(st.m_viewId, st.m_program.program);
}
// render camera frustums
@ -1300,7 +1409,7 @@ void Renderer::paintGL() {
bgfx::setIndexBuffer(cube_edges_ibh);
bgfx::setVertexBuffer(cube_vbh);
bgfx::setState(st.m_state);
bgfx::submit(st.m_viewId, st.m_program);
bgfx::submit(st.m_viewId, st.m_program.program);
}
// debug commands
@ -1366,7 +1475,7 @@ void Renderer::paintGL() {
bgfx::setIndexBuffer(debug_lines_ibh);
bgfx::setVertexBuffer(debug_lines_vbh);
bgfx::setState(st.m_state);
bgfx::submit(st.m_viewId, st.m_program);
bgfx::submit(st.m_viewId, st.m_program.program);
// free buffer data
delete[] line_vert_buf;
@ -1386,7 +1495,7 @@ void Renderer::paintGL() {
width,
height);
ImGui::SetNextWindowSize (ImVec2(400.f, 100.0f), ImGuiSetCond_Once);
ImGui::SetNextWindowSize (ImVec2(400.f, 200.0f), ImGuiSetCond_Once);
ImGui::SetNextWindowPos (ImVec2(10.f, 300.0f), ImGuiSetCond_Once);
ImGui::Begin("Render Settings");
@ -1410,6 +1519,7 @@ void Renderer::paintGL() {
assert (lights.size() == 1);
ImGui::Checkbox("Draw Floor", &drawFloor);
ImGui::Checkbox("Draw Debug", &drawDebug);
for (int i = 0; i < lights.size(); i++) {
@ -1426,6 +1536,28 @@ void Renderer::paintGL() {
debugCommands.clear();
}
bool Renderer::updateShaders() {
bool result = true;
for (int i = 0; i < RenderState::Count; i++) {
RenderState& st = s_renderStates[i];
if (st.m_program.vertexShaderFileName != ""
&& st.m_program.fragmentShaderFileName!= ""
) {
if (st.m_program.checkModified()) {
bool load_success = st.m_program.reload();
// if so far everything was successful but this one failed
// make sure to set the return value to false
if (result == true && ! load_success)
result = false;
}
}
}
return true;
}
Entity* Renderer::createEntity() {
Entity* result = new Entity();
entities.push_back(result);

View File

@ -193,6 +193,7 @@ struct DebugCommand {
struct Renderer {
bool initialized;
bool drawDebug;
bool drawFloor = true;
uint32_t width;
uint32_t height;
@ -237,6 +238,10 @@ struct Renderer {
void paintGLSimple();
void resize (int width, int height);
// check whether shader files were modified and reload them. Returns
// true on success, otherwise false
bool updateShaders();
Entity* createEntity();
bool destroyEntity (Entity* entity);
@ -254,12 +259,49 @@ struct Renderer {
const float &scale);
};
struct RenderProgram {
bgfx::ProgramHandle program;
std::string vertexShaderFileName;
int vertexShaderFileModTime;
std::string fragmentShaderFileName;
int fragmentShaderFileModTime;
RenderProgram () :
vertexShaderFileName(""),
vertexShaderFileModTime(-1),
fragmentShaderFileName(""),
fragmentShaderFileModTime(-1)
{
program = BGFX_INVALID_HANDLE;
}
RenderProgram (
const char* vertex_shader_file_name,
const char* fragment_shader_file_name
)
:
vertexShaderFileName(vertex_shader_file_name),
vertexShaderFileModTime(-1),
fragmentShaderFileName(fragment_shader_file_name),
fragmentShaderFileModTime(-1)
{
program = BGFX_INVALID_HANDLE;
}
bool reload();
bool checkModified() const;
bool valid() const {
return bgfx::isValid(program);
}
};
struct RenderState {
enum {
Skybox,
ShadowMap,
Scene,
SceneTextured,
Lines,
Debug,
Count
};
@ -273,7 +315,7 @@ struct RenderState {
uint64_t m_state;
uint8_t m_numTextures;
bgfx::ProgramHandle m_program;
RenderProgram m_program;
uint8_t m_viewId;
Texture m_textures[4];
};

View File

@ -240,7 +240,7 @@ bgfx::ProgramHandle loadProgramFromFiles(const char *_vsFileName, const char *_f
int result = compileShader (cmdLine, vs_source_reader, vs_compiled_writer);
if (result != EXIT_SUCCESS) {
std::cerr << "Error compiling shader " << _vsFileName << std::endl;
abort();
return BGFX_INVALID_HANDLE;
}
uint32_t size = vs_compiled_writer->seek(0, bx::Whence::End);
@ -269,7 +269,7 @@ bgfx::ProgramHandle loadProgramFromFiles(const char *_vsFileName, const char *_f
result = compileShader (cmdLine, fs_source_reader, fs_compiled_writer);
if (result != EXIT_SUCCESS) {
std::cerr << "Error compiling shader " << _fsFileName << std::endl;
abort();
return BGFX_INVALID_HANDLE;
}
uint32_t size = fs_compiled_writer->seek(0, bx::Whence::End);
@ -713,7 +713,7 @@ struct Mesh
bgfx::setIndexBuffer(group.m_ibh);
bgfx::setVertexBuffer(group.m_vbh);
bgfx::setState(state.m_state);
bgfx::submit(state.m_viewId, state.m_program);
bgfx::submit(state.m_viewId, state.m_program.program);
}
}
}