diff --git a/src/appleseed.studio/CMakeLists.txt b/src/appleseed.studio/CMakeLists.txt index 6073da93eb..38102430c6 100644 --- a/src/appleseed.studio/CMakeLists.txt +++ b/src/appleseed.studio/CMakeLists.txt @@ -365,6 +365,8 @@ set (utility_sources utility/doubleslider.h utility/foldablepanelwidget.cpp utility/foldablepanelwidget.h + utility/gl.cpp + utility/gl.h utility/inputwidgetproxies.cpp utility/inputwidgetproxies.h utility/interop.h diff --git a/src/appleseed.studio/main/main.cpp b/src/appleseed.studio/main/main.cpp index 61cc17f893..af52903c9e 100644 --- a/src/appleseed.studio/main/main.cpp +++ b/src/appleseed.studio/main/main.cpp @@ -329,7 +329,7 @@ int main(int argc, char* argv[]) // Set default surface format before creating application instance. This is // required on macOS in order to use an OpenGL Core profile context. QSurfaceFormat default_format; - default_format.setVersion(3, 3); + default_format.setVersion(4, 1); default_format.setProfile(QSurfaceFormat::CoreProfile); QSurfaceFormat::setDefaultFormat(default_format); diff --git a/src/appleseed.studio/mainwindow/mainwindow.cpp b/src/appleseed.studio/mainwindow/mainwindow.cpp index d6980c6c5f..715afd4b14 100644 --- a/src/appleseed.studio/mainwindow/mainwindow.cpp +++ b/src/appleseed.studio/mainwindow/mainwindow.cpp @@ -747,12 +747,12 @@ void MainWindow::update_workspace() m_project_manager.get_project()->get_light_path_recorder().get_light_path_count() > 0) { for (const_each i = m_viewport_tabs; i; ++i) - i->second->set_light_paths_enabled(true); + i->second->set_light_paths_toggle_enabled(true); } else { for (const_each i = m_viewport_tabs; i; ++i) - i->second->set_light_paths_enabled(false); + i->second->set_light_paths_toggle_enabled(false); } } diff --git a/src/appleseed.studio/mainwindow/rendering/glscenelayer.cpp b/src/appleseed.studio/mainwindow/rendering/glscenelayer.cpp index 58ab4e115b..67832c8714 100644 --- a/src/appleseed.studio/mainwindow/rendering/glscenelayer.cpp +++ b/src/appleseed.studio/mainwindow/rendering/glscenelayer.cpp @@ -30,7 +30,7 @@ #include "glscenelayer.h" // appleseed.studio headers. -#include "utility/miscellaneous.h" +#include "utility/gl.h" // appleseed.renderer headers. #include "renderer/api/camera.h" @@ -50,7 +50,7 @@ // Qt headers. #include -#include +#include #include #include #include @@ -137,7 +137,12 @@ GLSceneLayer::GLSceneLayer( set_transform(m_camera.transform_sequence().evaluate(time)); } -void GLSceneLayer::set_gl_functions(QOpenGLFunctions_3_3_Core* functions) +GLSceneLayer::~GLSceneLayer() +{ + cleanup_gl_data(); +} + +void GLSceneLayer::set_gl_functions(QOpenGLFunctions_4_1_Core* functions) { m_gl = functions; } @@ -364,100 +369,6 @@ void GLSceneLayer::load_scene_data() load_assembly_instance(assembly_instance, time); } -namespace { - const string shader_kind_to_string(const GLint shader_kind) - { - switch (shader_kind) { - case GL_VERTEX_SHADER: - return "Vertex"; - case GL_FRAGMENT_SHADER: - return "Fragment"; - default: - return "Unknown Kind"; - } - } - - void compile_shader( - QOpenGLFunctions_3_3_Core* f, - const GLuint shader, - const GLsizei count, - const GLchar** src_string, - const GLint* length) - { - f->glShaderSource(shader, count, src_string, length); - f->glCompileShader(shader); - GLint success; - f->glGetShaderiv(shader, GL_COMPILE_STATUS, &success); - - if (!success) - { - char info_log[1024]; - f->glGetShaderInfoLog(shader, 1024, NULL, info_log); - - GLint shader_kind; - f->glGetShaderiv(shader, GL_SHADER_TYPE, &shader_kind); - string shader_kind_string = shader_kind_to_string(shader_kind); - - RENDERER_LOG_ERROR("opengl: %s shader compilation failed:\n%s", shader_kind_string.c_str(), info_log); - } - } - - void link_shader_program( - QOpenGLFunctions_3_3_Core* f, - const GLuint program, - const GLuint vert, - const GLuint frag) - { - f->glAttachShader(program, vert); - - if (frag != 0) - f->glAttachShader(program, frag); - - f->glLinkProgram(program); - - GLint success; - f->glGetProgramiv(program, GL_LINK_STATUS, &success); - - if (!success) - { - char info_log[1024]; - f->glGetProgramInfoLog(program, 1024, NULL, info_log); - RENDERER_LOG_ERROR("opengl: shader program linking failed:\n%s", info_log); - } - } - - void create_shader_program( - QOpenGLFunctions_3_3_Core* f, - GLuint& program, - const QByteArray* vert_source, - const QByteArray* frag_source) - { - bool has_frag_shader = frag_source != nullptr; - - GLuint vert = f->glCreateShader(GL_VERTEX_SHADER); - GLuint frag = has_frag_shader ? f->glCreateShader(GL_FRAGMENT_SHADER) : 0; - - auto gl_vert_source = static_cast(vert_source->constData()); - auto gl_vert_source_length = static_cast(vert_source->size()); - - compile_shader(f, vert, 1, &gl_vert_source, &gl_vert_source_length); - - if (has_frag_shader) - { - auto gl_frag_source = static_cast(frag_source->constData()); - auto gl_frag_source_length = static_cast(frag_source->size()); - compile_shader(f, frag, 1, &gl_frag_source, &gl_frag_source_length); - } - - program = f->glCreateProgram(); - link_shader_program(f, program, vert, frag); - - f->glDeleteShader(vert); - if (has_frag_shader) - f->glDeleteShader(frag); - } -} - void GLSceneLayer::init_gl(QSurfaceFormat format) { // If there was already previous data, clean up diff --git a/src/appleseed.studio/mainwindow/rendering/glscenelayer.h b/src/appleseed.studio/mainwindow/rendering/glscenelayer.h index 6345a7a067..372208a261 100644 --- a/src/appleseed.studio/mainwindow/rendering/glscenelayer.h +++ b/src/appleseed.studio/mainwindow/rendering/glscenelayer.h @@ -58,7 +58,7 @@ namespace renderer { class ObjectInstance; } namespace renderer { class Project; } class QKeyEvent; class QImage; -class QOpenGLFunctions_3_3_Core; +class QOpenGLFunctions_4_1_Core; class QSurfaceFormat; namespace appleseed { @@ -79,6 +79,8 @@ class GLSceneLayer const size_t width, const size_t height); + ~GLSceneLayer(); + void init_gl( QSurfaceFormat format); @@ -86,7 +88,7 @@ class GLSceneLayer const foundation::Transformd& transform); void set_gl_functions( - QOpenGLFunctions_3_3_Core* functions); + QOpenGLFunctions_4_1_Core* functions); void draw(); void draw_depth_only(); @@ -103,7 +105,7 @@ class GLSceneLayer bool m_backface_culling_enabled; - QOpenGLFunctions_3_3_Core* m_gl; + QOpenGLFunctions_4_1_Core* m_gl; std::vector m_scene_object_data_vbos; std::vector m_scene_object_data_index_counts; @@ -125,7 +127,6 @@ class GLSceneLayer bool m_initialized; void render_scene(); - void cleanup_gl_data(); void load_scene_data(); diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathslayer.cpp b/src/appleseed.studio/mainwindow/rendering/lightpathslayer.cpp index 50ed9b135f..592f4fb630 100644 --- a/src/appleseed.studio/mainwindow/rendering/lightpathslayer.cpp +++ b/src/appleseed.studio/mainwindow/rendering/lightpathslayer.cpp @@ -30,7 +30,7 @@ #include "lightpathslayer.h" // appleseed.studio headers. -#include "utility/miscellaneous.h" +#include "utility/gl.h" // appleseed.renderer headers. #include "renderer/api/camera.h" @@ -50,7 +50,7 @@ // Qt headers. #include -#include +#include #include #include @@ -105,7 +105,7 @@ void LightPathsLayer::resize(const size_t width, const size_t height) m_height = height; } -void LightPathsLayer::set_gl_functions(QOpenGLFunctions_3_3_Core* functions) +void LightPathsLayer::set_gl_functions(QOpenGLFunctions_4_1_Core* functions) { m_gl = functions; } @@ -341,91 +341,6 @@ void LightPathsLayer::load_light_paths_data() } } -namespace { - const string shader_kind_to_string(const GLint shader_kind) - { - switch (shader_kind) { - case GL_VERTEX_SHADER: - return "Vertex"; - case GL_FRAGMENT_SHADER: - return "Fragment"; - default: - return "Unknown Kind"; - } - } - - void compile_shader( - QOpenGLFunctions_3_3_Core* f, - const GLuint shader, - const GLsizei count, - const GLchar** src_string, - const GLint* length) - { - f->glShaderSource(shader, count, src_string, length); - f->glCompileShader(shader); - GLint success; - f->glGetShaderiv(shader, GL_COMPILE_STATUS, &success); - - if (!success) - { - char info_log[1024]; - f->glGetShaderInfoLog(shader, 1024, NULL, info_log); - - GLint shader_kind; - f->glGetShaderiv(shader, GL_SHADER_TYPE, &shader_kind); - string shader_kind_string = shader_kind_to_string(shader_kind); - - RENDERER_LOG_ERROR("opengl: %s shader compilation failed:\n%s", shader_kind_string.c_str(), info_log); - } - } - - void link_shader_program( - QOpenGLFunctions_3_3_Core* f, - const GLuint program, - const GLuint vert, - const GLuint frag) - { - f->glAttachShader(program, vert); - f->glAttachShader(program, frag); - f->glLinkProgram(program); - - GLint success; - f->glGetProgramiv(program, GL_LINK_STATUS, &success); - - if (!success) - { - char info_log[1024]; - f->glGetProgramInfoLog(program, 1024, NULL, info_log); - RENDERER_LOG_ERROR("opengl: shader program linking failed:\n%s", info_log); - } - } - - void create_shader_program( - QOpenGLFunctions_3_3_Core* f, - GLuint& program, - const QByteArray& vert_source, - const QByteArray& frag_source) - { - GLuint vert = f->glCreateShader(GL_VERTEX_SHADER); - GLuint frag = f->glCreateShader(GL_FRAGMENT_SHADER); - - auto gl_vert_source = static_cast(vert_source.constData()); - auto gl_vert_source_length = static_cast(vert_source.size()); - - auto gl_frag_source = static_cast(frag_source.constData()); - auto gl_frag_source_length = static_cast(frag_source.size()); - - compile_shader(f, vert, 1, &gl_vert_source, &gl_vert_source_length); - compile_shader(f, frag, 1, &gl_frag_source, &gl_frag_source_length); - - program = f->glCreateProgram(); - link_shader_program(f, program, vert, frag); - - f->glDeleteShader(vert); - f->glDeleteShader(frag); - } -} - void LightPathsLayer::init_gl(QSurfaceFormat format) { // If there was already previous data, clean up @@ -441,8 +356,8 @@ void LightPathsLayer::init_gl(QSurfaceFormat format) create_shader_program( m_gl, m_shader_program, - vertex_shader, - fragment_shader); + &vertex_shader, + &fragment_shader); m_view_mat_loc = m_gl->glGetUniformLocation(m_shader_program, "u_view"); m_proj_mat_loc = m_gl->glGetUniformLocation(m_shader_program, "u_proj"); @@ -586,20 +501,14 @@ void LightPathsLayer::draw_render_camera() const { auto gl_view_matrix = const_cast(&m_gl_render_view_matrix[0]); - m_gl->glEnable(GL_BLEND); - m_gl->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); render_scene(gl_view_matrix); - m_gl->glDisable(GL_BLEND); } void LightPathsLayer::draw() const { auto gl_view_matrix = const_cast(&m_gl_view_matrix[0]); - m_gl->glEnable(GL_BLEND); - m_gl->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); render_scene(gl_view_matrix); - m_gl->glDisable(GL_BLEND); } void LightPathsLayer::render_scene(const GLfloat* gl_view_matrix) const diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathslayer.h b/src/appleseed.studio/mainwindow/rendering/lightpathslayer.h index 6908a399cd..a3a5d38dc4 100644 --- a/src/appleseed.studio/mainwindow/rendering/lightpathslayer.h +++ b/src/appleseed.studio/mainwindow/rendering/lightpathslayer.h @@ -58,7 +58,7 @@ namespace renderer { class ObjectInstance; } namespace renderer { class Project; } class QKeyEvent; class QImage; -class QOpenGLFunctions_3_3_Core; +class QOpenGLFunctions_4_1_Core; namespace appleseed { namespace studio { @@ -91,7 +91,7 @@ class LightPathsLayer: public QObject const int selected_light_path_index); void set_gl_functions( - QOpenGLFunctions_3_3_Core* functions); + QOpenGLFunctions_4_1_Core* functions); void init_gl(QSurfaceFormat format); @@ -127,7 +127,7 @@ class LightPathsLayer: public QObject size_t m_width; size_t m_height; - QOpenGLFunctions_3_3_Core* m_gl; + QOpenGLFunctions_4_1_Core* m_gl; GLuint m_positions_vbo; GLuint m_others_vbo; diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.cpp b/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.cpp index ab67077b70..d9c6911276 100644 --- a/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.cpp +++ b/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.cpp @@ -87,7 +87,7 @@ LightPathsViewportManager::LightPathsViewportManager( LightPathsLayer* light_paths_layer = m_viewport_tab->get_viewport_widget()->get_light_paths_layer(); connect( light_paths_layer, SIGNAL(signal_light_path_selection_changed(const int, const int)), - SLOT(slot_light_path_selection_changed)); + SLOT(slot_light_path_selection_changed(const int, const int))); create_toolbar(); @@ -101,6 +101,7 @@ void LightPathsViewportManager::set_enabled(const bool enabled) m_toolbar->show(); else m_toolbar->hide(); + m_toolbar->setDisabled(!enabled); m_screen_space_paths_picking_handler->set_enabled(enabled); } @@ -115,7 +116,6 @@ void LightPathsViewportManager::slot_entity_picked(const ScenePicker::PickingRes if (!m_enabled) return; const CanvasProperties& props = m_project.get_frame()->image().properties(); - m_screen_space_paths_picking_handler->pick( Vector2i( result.m_ndc[0] * static_cast(props.m_canvas_width), @@ -189,14 +189,6 @@ void LightPathsViewportManager::create_toolbar() m_toolbar->setObjectName("render_toolbar"); m_toolbar->setIconSize(QSize(18, 18)); - //// Pick paths button - //QToolButton* m_pick_paths_button = new QToolButton(); - //m_pick_paths_button->setText("Pick Light Paths"); - //m_pick_paths_button->setToolTip("Pick Light Paths"); - //connect( - // m_pick_paths_button, SIGNAL(clicked()), - // SIGNAL(slot_light_paths_pick_button_clicked)); - // Save Light Paths button. QToolButton* save_light_paths_button = new QToolButton(); save_light_paths_button->setIcon(load_icons("lightpathstab_save_light_paths")); diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.h b/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.h index 692b989760..d61746d4d5 100644 --- a/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.h +++ b/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.h @@ -98,7 +98,6 @@ class LightPathsViewportManager renderer::ParamArray& m_settings; ViewportTab* m_viewport_tab; QToolBar* m_toolbar; - //QToolButton* m_pick_paths_button; QToolButton* m_prev_path_button; QToolButton* m_next_path_button; QLabel* m_info_label; @@ -110,7 +109,6 @@ class LightPathsViewportManager void create_toolbar(); void recreate_handlers(); - }; } // namespace studio diff --git a/src/appleseed.studio/mainwindow/rendering/renderlayer.cpp b/src/appleseed.studio/mainwindow/rendering/renderlayer.cpp index 6238e5bda3..93fafdd588 100644 --- a/src/appleseed.studio/mainwindow/rendering/renderlayer.cpp +++ b/src/appleseed.studio/mainwindow/rendering/renderlayer.cpp @@ -40,6 +40,7 @@ #include "foundation/image/tile.h" #include "foundation/math/scalar.h" #include "foundation/platform/types.h" +#include "utility/gl.h" // Qt headers. #include @@ -48,6 +49,8 @@ #include #include #include +#include +#include #include #include @@ -74,11 +77,14 @@ RenderLayer::RenderLayer( : QWidget(parent) , m_mutex(QMutex::Recursive) , m_ocio_config(ocio_config) + , m_gl_initialized(false) { setFocusPolicy(Qt::StrongFocus); setAutoFillBackground(false); setAttribute(Qt::WA_OpaquePaintEvent, true); + m_gl_tex = new QOpenGLTexture(QOpenGLTexture::Target2D); + resize(width, height); const char* display_name = m_ocio_config->getDefaultDisplay(); @@ -88,6 +94,64 @@ RenderLayer::RenderLayer( setAcceptDrops(true); } +void RenderLayer::draw(GLuint empty_vao, bool paths_display_active) +{ + QMutexLocker locker(&m_mutex); + + if (m_gl_tex->width() != m_image.width() || m_gl_tex->height() != m_image.height()) + { + if (m_gl_tex->isCreated()) + m_gl_tex->destroy(); + + m_gl_tex->create(); + m_gl_tex->setSize(m_image.width(), m_image.height()); + m_gl_tex->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Nearest); + } + + m_gl_tex->setData(m_image, QOpenGLTexture::MipMapGeneration::DontGenerateMipMaps); + + m_gl->glUseProgram(m_shader_program); + + GLfloat mult = paths_display_active ? 0.6 : 1.0; + m_gl->glUniform1f(m_mult_loc, mult); + + m_gl->glActiveTexture(GL_TEXTURE0); + m_gl_tex->bind(); + m_gl->glDisable(GL_DEPTH_TEST); + m_gl->glDepthMask(GL_FALSE); + m_gl->glBindVertexArray(empty_vao); + m_gl->glDrawArrays(GL_TRIANGLES, 0, 3); + m_gl->glDepthMask(GL_TRUE); +} + + +void RenderLayer::init_gl(QSurfaceFormat format) +{ + if (!m_gl) + { + RENDERER_LOG_ERROR("Attempted to initialize GL without first setting GL functions"); + return; + } + + auto vertex_shader = load_gl_shader("fullscreen_tri.vert"); + auto fragment_shader = load_gl_shader("final_render.frag"); + + create_shader_program( + m_gl, + m_shader_program, + &vertex_shader, + &fragment_shader); + + m_mult_loc = m_gl->glGetUniformLocation(m_shader_program, "u_mult"); + + m_gl_initialized = true; +} + +void RenderLayer::set_gl_functions(QOpenGLFunctions_4_1_Core* functions) +{ + m_gl = functions; +} + QImage RenderLayer::capture() { QMutexLocker locker(&m_mutex); @@ -425,12 +489,5 @@ void RenderLayer::update_tile_no_lock(const size_t tile_x, const size_t tile_y) NativeDrawing::blit(dest, dest_stride, uint8_rgb_tile); } -void RenderLayer::paint(const QRect& rect, QPainter& painter) -{ - QMutexLocker locker(&m_mutex); - - painter.drawImage(rect, m_image); -} - } // namespace studio } // namespace appleseed diff --git a/src/appleseed.studio/mainwindow/rendering/renderlayer.h b/src/appleseed.studio/mainwindow/rendering/renderlayer.h index f2920c334d..c6af7778f9 100644 --- a/src/appleseed.studio/mainwindow/rendering/renderlayer.h +++ b/src/appleseed.studio/mainwindow/rendering/renderlayer.h @@ -44,6 +44,7 @@ namespace OCIO = OCIO_NAMESPACE; // Qt headers. #include #include +#include #include #include @@ -58,6 +59,8 @@ namespace renderer { class Frame; } class QDragEnterEvent; class QDragMoveEvent; class QDropEvent; +class QOpenGLFunctions_4_1_Core; +class QOpenGLTexture; class QPaintEvent; class QRect; @@ -117,8 +120,10 @@ class RenderLayer // Thread-safe. void blit_frame(const renderer::Frame& frame); - // Thread-safe. Paint self into specified painter. - void paint(const QRect& rect, QPainter& painter); + void draw(GLuint empty_vao, bool paths_display_active); + void init_gl(QSurfaceFormat format); + void set_gl_functions( + QOpenGLFunctions_4_1_Core* functions); // Direct access to internals for high-performance drawing. QMutex& mutex(); @@ -135,11 +140,16 @@ class RenderLayer private: mutable QMutex m_mutex; QImage m_image; - QPainter m_painter; std::unique_ptr m_float_tile_storage; std::unique_ptr m_uint8_tile_storage; std::unique_ptr m_image_storage; + QOpenGLFunctions_4_1_Core* m_gl; + QOpenGLTexture* m_gl_tex; + GLuint m_shader_program; + GLint m_mult_loc; + bool m_gl_initialized; + OCIO::ConstConfigRcPtr m_ocio_config; OCIO::ConstProcessorRcPtr m_ocio_processor; diff --git a/src/appleseed.studio/mainwindow/rendering/viewporttab.cpp b/src/appleseed.studio/mainwindow/rendering/viewporttab.cpp index 37ff7f796c..9ba95e517a 100644 --- a/src/appleseed.studio/mainwindow/rendering/viewporttab.cpp +++ b/src/appleseed.studio/mainwindow/rendering/viewporttab.cpp @@ -132,8 +132,8 @@ void ViewportTab::set_render_region_buttons_enabled(const bool enabled) void ViewportTab::render_began() { - get_viewport_widget()->get_render_layer()->darken(); - get_viewport_widget()->get_light_paths_layer()->update_render_camera_transform(); + m_viewport_widget->get_render_layer()->darken(); + m_viewport_widget->get_light_paths_layer()->update_render_camera_transform(); set_light_paths_enabled(false); update(); } @@ -189,17 +189,16 @@ void ViewportTab::slot_base_layer_changed(int index) switch (base_layer) { - case ViewportWidget::BaseLayer::FinalRender: - if (m_rendering_manager.is_rendering() - && m_rendering_manager.get_rendering_mode() == RenderingManager::RenderingMode::InteractiveRendering) - m_camera_controller.get()->set_enabled(true); - else - m_camera_controller.get()->set_enabled(false); - break; - - case ViewportWidget::BaseLayer::OpenGL: + case ViewportWidget::BaseLayer::FinalRender: + if (m_rendering_manager.is_rendering() + && m_rendering_manager.get_rendering_mode() == RenderingManager::RenderingMode::InteractiveRendering) m_camera_controller.get()->set_enabled(true); - break; + else + m_camera_controller.get()->set_enabled(false); + break; + case ViewportWidget::BaseLayer::OpenGL: + m_camera_controller.get()->set_enabled(true); + break; } } @@ -298,14 +297,6 @@ void ViewportTab::create_toolbar() m_light_paths_toggle_button->setText("Display Light Paths Overlay"); m_light_paths_toggle_button->setCheckable(true); m_toolbar->addWidget(m_light_paths_toggle_button); - connect( - m_light_paths_toggle_button, SIGNAL(toggled(bool)), - m_viewport_widget, SLOT(slot_light_paths_toggled(bool)) - ); - connect( - m_light_paths_toggle_button, SIGNAL(toggled(bool)), - m_light_paths_manager, SLOT(slot_light_paths_display_toggled(bool)) - ); m_toolbar->addSeparator(); diff --git a/src/appleseed.studio/mainwindow/rendering/viewportwidget.cpp b/src/appleseed.studio/mainwindow/rendering/viewportwidget.cpp index 9ca818212c..01b69c4425 100644 --- a/src/appleseed.studio/mainwindow/rendering/viewportwidget.cpp +++ b/src/appleseed.studio/mainwindow/rendering/viewportwidget.cpp @@ -29,6 +29,9 @@ // Interface header. #include "viewportwidget.h" +// appleseed.studio headers. +#include "utility/gl.h" + // appleseed.renderer headers. #include "renderer/api/frame.h" @@ -47,7 +50,8 @@ #include #include #include -#include +#include +#include #include #include @@ -76,6 +80,12 @@ ViewportWidget::ViewportWidget( , m_project(project) , m_draw_light_paths(false) , m_active_base_layer(static_cast(0)) + , m_resolve_program(0) + , m_accum_loc(0) + , m_accum_tex(0) + , m_revealage_loc(0) + , m_revealage_tex(0) + , m_accum_revealage_fb(0) { setFocusPolicy(Qt::StrongFocus); setFixedWidth(static_cast(width)); @@ -92,6 +102,17 @@ ViewportWidget::ViewportWidget( setAcceptDrops(true); } +ViewportWidget::~ViewportWidget() +{ + m_gl->glDeleteProgram(m_resolve_program); + m_gl->glDeleteTextures(1, &m_accum_tex); + m_gl->glDeleteTextures(1, &m_revealage_tex); + m_gl->glDeleteTextures(1, &m_color_tex); + m_gl->glDeleteTextures(1, &m_depth_tex); + m_gl->glDeleteFramebuffers(1, &m_accum_revealage_fb); + m_gl->glDeleteFramebuffers(1, &m_main_fb); +} + QString ViewportWidget::base_layer_string(BaseLayer layer) { switch (layer) @@ -153,7 +174,7 @@ QImage ViewportWidget::capture() void ViewportWidget::initializeGL() { RENDERER_LOG_INFO("initializing opengl."); - m_gl = QOpenGLContext::currentContext()->versionFunctions(); + m_gl = QOpenGLContext::currentContext()->versionFunctions(); const auto qs_format = format(); if (!m_gl->initializeOpenGLFunctions()) @@ -167,6 +188,60 @@ void ViewportWidget::initializeGL() { return; } + m_gl->glGenVertexArrays(1, &m_empty_vao); + m_gl->glBindVertexArray(m_empty_vao); + m_gl->glGenBuffers(1, &m_empty_vbo); + m_gl->glBindBuffer(GL_ARRAY_BUFFER, m_empty_vbo); + float vals[3]{ 0.0f, 0.0f, 0.0f }; + m_gl->glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3, static_cast(vals), GL_STATIC_DRAW); + m_gl->glVertexAttribPointer(0, sizeof(float), GL_FLOAT, GL_FALSE, sizeof(float), static_cast(0)); + m_gl->glEnableVertexAttribArray(0); + + auto vertex_shader = load_gl_shader("fullscreen_tri.vert"); + auto fragment_shader = load_gl_shader("oit_resolve.frag"); + + create_shader_program( + m_gl, + m_resolve_program, + &vertex_shader, + &fragment_shader); + + m_accum_loc = m_gl->glGetUniformLocation(m_resolve_program, "u_accum_tex"); + m_revealage_loc = m_gl->glGetUniformLocation(m_resolve_program, "u_revealage_tex"); + + GLuint temp_fbs[2]; + m_gl->glGenFramebuffers(2, temp_fbs); + + m_main_fb = temp_fbs[0]; + m_accum_revealage_fb = temp_fbs[1]; + + GLuint temp_texs[4]; + m_gl->glGenTextures(4, temp_texs); + + m_color_tex = temp_texs[0]; + m_depth_tex = temp_texs[1]; + m_accum_tex = temp_texs[2]; + m_revealage_tex = temp_texs[3]; + + resizeGL(width(), height()); + + m_gl->glBindFramebuffer(GL_FRAMEBUFFER, m_main_fb); + m_gl->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_color_tex, 0); + m_gl->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_depth_tex, 0); + + m_gl->glBindFramebuffer(GL_FRAMEBUFFER, m_accum_revealage_fb); + m_gl->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_accum_tex, 0); + m_gl->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, m_revealage_tex, 0); + m_gl->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_depth_tex, 0); + + m_gl->glBindFramebuffer(GL_FRAMEBUFFER, 0); + + m_gl->glUseProgram(m_resolve_program); + m_gl->glUniform1i(m_accum_loc, 0); + m_gl->glUniform1i(m_revealage_loc, 1); + + m_render_layer->set_gl_functions(m_gl); + m_render_layer->init_gl(qs_format); m_gl_scene_layer->set_gl_functions(m_gl); m_gl_scene_layer->init_gl(qs_format); m_light_paths_layer->set_gl_functions(m_gl); @@ -186,6 +261,28 @@ void ViewportWidget::resizeGL( int height) { m_light_paths_layer->resize(width, height); + + m_gl->glBindTexture(GL_TEXTURE_2D, m_color_tex); + m_gl->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + m_gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + m_gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + m_gl->glBindTexture(GL_TEXTURE_2D, m_depth_tex); + m_gl->glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL); + m_gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + m_gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + m_gl->glBindTexture(GL_TEXTURE_2D, m_accum_tex); + m_gl->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_HALF_FLOAT, NULL); + m_gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + m_gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + m_gl->glBindTexture(GL_TEXTURE_2D, m_revealage_tex); + m_gl->glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, width, height, 0, GL_RED, GL_HALF_FLOAT, NULL); + m_gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + m_gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + m_gl->glBindTexture(GL_TEXTURE_2D, 0); } void ViewportWidget::set_draw_light_paths_enabled(const bool enabled) @@ -196,14 +293,25 @@ void ViewportWidget::set_draw_light_paths_enabled(const bool enabled) void ViewportWidget::paintGL() { + + double dpr = static_cast(m_render_layer->image().devicePixelRatio()); + GLsizei w = static_cast(width() * dpr); + GLsizei h = static_cast(height() * dpr); + m_gl->glViewport(0, 0, w, h); + + m_gl->glBindFramebuffer(GL_FRAMEBUFFER, m_main_fb); + // Clear the main framebuffers + GLfloat main_clear[]{ 0.0, 0.0, 0.0, 0.0 }; + m_gl->glClearBufferfv(GL_COLOR, 0, main_clear); + GLfloat depth_clear = 1.0; + m_gl->glClearBufferfi(GL_DEPTH_STENCIL, 0, 1.0, 0); + if (m_active_base_layer == BaseLayer::FinalRender) { - m_painter.begin(this); - m_render_layer->paint(rect(), m_painter); - m_painter.end(); + m_render_layer->draw(m_empty_vao, m_draw_light_paths); } - m_gl->glViewport(0, 0, width(), height()); + QOpenGLContext *ctx = const_cast(QOpenGLContext::currentContext()); if (m_active_base_layer == BaseLayer::OpenGL || m_draw_light_paths) m_gl_scene_layer->draw_depth_only(); @@ -213,11 +321,60 @@ void ViewportWidget::paintGL() if (m_draw_light_paths) { + // Bind accum/revealage framebuffer + m_gl->glBindFramebuffer(GL_FRAMEBUFFER, m_accum_revealage_fb); + + // Set both attachments as active draw buffers + const GLenum buffers[]{ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; + m_gl->glDrawBuffers(2, buffers); + + // Clear the buffers + GLfloat accum_clear_col[]{ 0.0, 0.0, 0.0, 0.0 }; + m_gl->glClearBufferfv(GL_COLOR, 0, accum_clear_col); + GLfloat revealage_clear_col[]{ 1.0, 0.0, 0.0, 0.0 }; + m_gl->glClearBufferfv(GL_COLOR, 1, revealage_clear_col); + + // Enable proper blending for each + m_gl->glEnable(GL_BLEND); + m_gl->glBlendFunci(0, GL_ONE, GL_ONE); + m_gl->glBlendFunci(1, GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + m_gl->glEnable(GL_DEPTH_TEST); + m_gl->glDepthMask(GL_FALSE); + if (m_active_base_layer == BaseLayer::FinalRender) m_light_paths_layer->draw_render_camera(); else m_light_paths_layer->draw(); + + m_gl->glUseProgram(m_resolve_program); + + // Set default framebuffer object + m_gl->glBindFramebuffer(GL_FRAMEBUFFER, m_main_fb); + m_gl->glDrawBuffer(GL_COLOR_ATTACHMENT0); + + m_gl->glBindVertexArray(m_empty_vao); + + m_gl->glActiveTexture(GL_TEXTURE0); + m_gl->glBindTexture(GL_TEXTURE_2D, m_accum_tex); + m_gl->glActiveTexture(GL_TEXTURE1); + m_gl->glBindTexture(GL_TEXTURE_2D, m_revealage_tex); + + m_gl->glDisable(GL_DEPTH_TEST); + m_gl->glDepthMask(GL_FALSE); + m_gl->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + m_gl->glDrawArrays(GL_TRIANGLES, 0, 3); + + m_gl->glActiveTexture(GL_TEXTURE1); + m_gl->glBindTexture(GL_TEXTURE_2D, 0); + m_gl->glActiveTexture(GL_TEXTURE0); + m_gl->glBindTexture(GL_TEXTURE_2D, 0); + m_gl->glDepthMask(GL_TRUE); } + + m_gl->glBindFramebuffer(GL_READ_FRAMEBUFFER, m_main_fb); + m_gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, ctx->defaultFramebufferObject()); + m_gl->glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); } void ViewportWidget::dragEnterEvent(QDragEnterEvent* event) diff --git a/src/appleseed.studio/mainwindow/rendering/viewportwidget.h b/src/appleseed.studio/mainwindow/rendering/viewportwidget.h index 43568b2f76..94f02aef89 100644 --- a/src/appleseed.studio/mainwindow/rendering/viewportwidget.h +++ b/src/appleseed.studio/mainwindow/rendering/viewportwidget.h @@ -63,7 +63,7 @@ class QDragEnterEvent; class QDragMoveEvent; class QDropEvent; class QPaintEvent; -class QOpenGLFunctions_3_3_Core; +class QOpenGLFunctions_4_1_Core; namespace appleseed { namespace studio { @@ -87,6 +87,8 @@ class ViewportWidget OCIO::ConstConfigRcPtr ocio_config, QWidget* parent = nullptr); + ~ViewportWidget(); + enum BaseLayer { FinalRender, OpenGL, @@ -123,9 +125,22 @@ class ViewportWidget int m_width; int m_height; - QOpenGLFunctions_3_3_Core* m_gl; + QOpenGLFunctions_4_1_Core* m_gl; QPainter m_painter; + GLuint m_depth_tex; + GLuint m_color_tex; + GLuint m_accum_tex; + GLuint m_revealage_tex; + GLuint m_main_fb; + GLuint m_accum_revealage_fb; + GLuint m_empty_vao; + GLuint m_empty_vbo; + + GLuint m_resolve_program; + GLint m_accum_loc; + GLint m_revealage_loc; + std::unique_ptr m_render_layer; std::unique_ptr m_gl_scene_layer; std::unique_ptr m_light_paths_layer; diff --git a/src/appleseed.studio/resources/resources.qrc b/src/appleseed.studio/resources/resources.qrc index a55b80d91f..d4833e1d22 100644 --- a/src/appleseed.studio/resources/resources.qrc +++ b/src/appleseed.studio/resources/resources.qrc @@ -1,8 +1,11 @@ images/appleseed-logo-256.png + shaders/final_render.frag + shaders/fullscreen_tri.vert shaders/lightpaths.frag shaders/lightpaths.vert + shaders/oit_resolve.frag shaders/scene.frag shaders/scene.vert widgets/checkbox_checked_disabled.png diff --git a/src/appleseed.studio/resources/shaders/final_render.frag b/src/appleseed.studio/resources/shaders/final_render.frag new file mode 100644 index 0000000000..6f9eadf6d7 --- /dev/null +++ b/src/appleseed.studio/resources/shaders/final_render.frag @@ -0,0 +1,41 @@ +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#version 410 + +in vec2 f_uv; + +uniform sampler2D u_render_tex; +uniform float u_mult; + +out vec4 Target0; + +void main() { + vec3 col = texture(u_render_tex, f_uv, 0).rgb; + + Target0 = vec4(col * u_mult, 1.0); +} diff --git a/src/appleseed.studio/resources/shaders/fullscreen_tri.vert b/src/appleseed.studio/resources/shaders/fullscreen_tri.vert new file mode 100644 index 0000000000..7dbb4b2100 --- /dev/null +++ b/src/appleseed.studio/resources/shaders/fullscreen_tri.vert @@ -0,0 +1,48 @@ +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#version 410 + +const vec2 verts[3] = vec2[]( + vec2(3.0, 1.0), + vec2(-1.0, -3.0), + vec2(-1.0, 1.0) +); + +const vec2 uvs[3] = vec2[]( + vec2(2.0, 0.0), + vec2(0.0, 2.0), + vec2(0.0, 0.0) +); + +out vec2 f_uv; + +void main() +{ + f_uv = uvs[gl_VertexID]; + gl_Position = vec4(verts[gl_VertexID], 0.0, 1.0); +} \ No newline at end of file diff --git a/src/appleseed.studio/resources/shaders/lightpaths.frag b/src/appleseed.studio/resources/shaders/lightpaths.frag index 01d3191617..ea8d3dc0ee 100644 --- a/src/appleseed.studio/resources/shaders/lightpaths.frag +++ b/src/appleseed.studio/resources/shaders/lightpaths.frag @@ -25,7 +25,7 @@ // THE SOFTWARE. // -#version 330 +#version 410 #extension GL_ARB_separate_shader_objects : enable flat in vec3 f_color; @@ -36,12 +36,26 @@ flat in float f_aspect_expansion_len; uniform vec2 u_res; -out vec4 Target0; +layout(location = 0) out vec4 accum_target; +layout(location = 1) out float revealage_target; +void write_pixel(vec4 premultiplied_col, vec3 transmit) { + // premultiplied_col.a *= 1.0 - clamp((transmit.r + transmit.g + transmit.b) * (1.0 / 3.0), 0, 1); + + float a = min(1.0, premultiplied_col.a) * 8.0 + 0.01; + float b = -gl_FragCoord.z * 0.95 + 1.0; + + float w = clamp(a * a * a * 1e8 * b * b * b, 1e-2, 3e2); + + accum_target = premultiplied_col * w; + revealage_target = premultiplied_col.a; +} void main() { float dist = abs(f_aa_norm) * f_total_thickness - 0.5 / f_aspect_expansion_len; float eps = fwidth(dist); float a = 1.0 - smoothstep(f_thickness - eps, f_thickness + eps, dist); - Target0 = vec4(f_color, a); + + vec4 premult = vec4(f_color * a, a); + write_pixel(premult, vec3(1.0)); } diff --git a/src/appleseed.studio/resources/shaders/lightpaths.vert b/src/appleseed.studio/resources/shaders/lightpaths.vert index 579c386a39..35a409ae54 100644 --- a/src/appleseed.studio/resources/shaders/lightpaths.vert +++ b/src/appleseed.studio/resources/shaders/lightpaths.vert @@ -25,7 +25,7 @@ // THE SOFTWARE. // -#version 330 +#version 410 const float AA_BUFFER_SIZE = 1.0; diff --git a/src/appleseed.studio/resources/shaders/oit_resolve.frag b/src/appleseed.studio/resources/shaders/oit_resolve.frag new file mode 100644 index 0000000000..3a091797f8 --- /dev/null +++ b/src/appleseed.studio/resources/shaders/oit_resolve.frag @@ -0,0 +1,52 @@ +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#version 410 + +uniform sampler2D u_accum_tex; +uniform sampler2D u_revealage_tex; + +out vec4 Target0; + +float max4(vec4 v) { + return max(max(max(v.x, v.y), v.z), v.w); +} + +void main() { + ivec2 coord = ivec2(gl_FragCoord.xy); + + float revealage = texelFetch(u_revealage_tex, coord, 0).r; + + vec4 accum = texelFetch(u_accum_tex, coord, 0); + // Suppress overflow + if (isinf(max4(abs(accum)))) { + accum.rgb = vec3(accum.a); + } + vec3 averageColor = accum.rgb / max(accum.a, 0.00001); + + Target0 = vec4(averageColor, 1.0 - revealage); +} diff --git a/src/appleseed.studio/resources/shaders/scene.frag b/src/appleseed.studio/resources/shaders/scene.frag index 2dc350f5d2..d58363f361 100644 --- a/src/appleseed.studio/resources/shaders/scene.frag +++ b/src/appleseed.studio/resources/shaders/scene.frag @@ -25,7 +25,7 @@ // THE SOFTWARE. // -#version 330 +#version 410 #extension GL_ARB_separate_shader_objects : enable layout(location = 0) in vec3 v_world_pos; diff --git a/src/appleseed.studio/resources/shaders/scene.vert b/src/appleseed.studio/resources/shaders/scene.vert index 18f61388f8..a1bdde9adc 100644 --- a/src/appleseed.studio/resources/shaders/scene.vert +++ b/src/appleseed.studio/resources/shaders/scene.vert @@ -25,7 +25,7 @@ // THE SOFTWARE. // -#version 330 +#version 410 #extension GL_ARB_separate_shader_objects : enable layout(location = 0) in vec3 a_pos; diff --git a/src/appleseed.studio/utility/gl.cpp b/src/appleseed.studio/utility/gl.cpp new file mode 100644 index 0000000000..3ed09c6725 --- /dev/null +++ b/src/appleseed.studio/utility/gl.cpp @@ -0,0 +1,149 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#include "gl.h" + +// appleseed.studio headers. +#include "renderer/api/utility.h" +#include "utility/miscellaneous.h" + +// Qt headers. +#include +#include +#include + +using namespace std; +using namespace renderer; + +namespace appleseed { +namespace studio { + +const string shader_kind_to_string(const GLint shader_kind) +{ + switch (shader_kind) { + case GL_VERTEX_SHADER: + return "Vertex"; + case GL_FRAGMENT_SHADER: + return "Fragment"; + } + return "Unknown Kind"; +} + +void compile_shader( + QOpenGLFunctions_4_1_Core* f, + const GLuint shader, + const GLsizei count, + const GLchar** src_string, + const GLint* length) +{ + f->glShaderSource(shader, count, src_string, length); + f->glCompileShader(shader); + GLint success; + f->glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + + if (!success) + { + char info_log[1024]; + f->glGetShaderInfoLog(shader, 1024, NULL, info_log); + + GLint shader_kind; + f->glGetShaderiv(shader, GL_SHADER_TYPE, &shader_kind); + string shader_kind_string = shader_kind_to_string(shader_kind); + + RENDERER_LOG_ERROR("opengl: %s shader compilation failed:\n%s", shader_kind_string.c_str(), info_log); + } +} + +void link_shader_program( + QOpenGLFunctions_4_1_Core* f, + const GLuint program, + const GLuint vert, + const GLuint frag) +{ + f->glAttachShader(program, vert); + + if (frag != 0) + f->glAttachShader(program, frag); + + f->glLinkProgram(program); + + GLint success; + f->glGetProgramiv(program, GL_LINK_STATUS, &success); + + if (!success) + { + char info_log[1024]; + f->glGetProgramInfoLog(program, 1024, NULL, info_log); + RENDERER_LOG_ERROR("opengl: shader program linking failed:\n%s", info_log); + } +} + +void create_shader_program( + QOpenGLFunctions_4_1_Core* f, + GLuint& program, + const QByteArray* vert_source, + const QByteArray* frag_source) +{ + assert(vert_source != nullptr); + bool has_frag_shader = frag_source != nullptr; + + GLuint vert = f->glCreateShader(GL_VERTEX_SHADER); + GLuint frag = has_frag_shader ? f->glCreateShader(GL_FRAGMENT_SHADER) : 0; + + auto gl_vert_source = static_cast(vert_source->constData()); + auto gl_vert_source_length = static_cast(vert_source->size()); + + compile_shader(f, vert, 1, &gl_vert_source, &gl_vert_source_length); + + if (has_frag_shader) + { + auto gl_frag_source = static_cast(frag_source->constData()); + auto gl_frag_source_length = static_cast(frag_source->size()); + compile_shader(f, frag, 1, &gl_frag_source, &gl_frag_source_length); + } + + program = f->glCreateProgram(); + link_shader_program(f, program, vert, frag); + + f->glDeleteShader(vert); + if (has_frag_shader) + f->glDeleteShader(frag); +} + +QByteArray load_gl_shader(const QString& base_name) +{ + const QString resource_path(QString(":/shaders/%1").arg(base_name)); + + QFile file(resource_path); + file.open(QFile::ReadOnly); + + return file.readAll(); +} + +} // namespace studio +} // namespace appleseed diff --git a/src/appleseed.studio/utility/gl.h b/src/appleseed.studio/utility/gl.h new file mode 100644 index 0000000000..b63b4c583c --- /dev/null +++ b/src/appleseed.studio/utility/gl.h @@ -0,0 +1,75 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#pragma once + +// Qt headers. +#include +#include + +// standard headers. +#include + +// Forward declarations. +class QByteArray; +class QString; + +namespace appleseed { +namespace studio { + +// Get a string from an OpenGL shader kind value. +const std::string shader_kind_to_string(const GLint shader_kind); + +// Compile a GL shader. +void compile_shader( + QOpenGLFunctions_4_1_Core* f, + const GLuint shader, + const GLsizei count, + const GLchar** src_string, + const GLint* length); + +// Link a GL shader program. +void link_shader_program( + QOpenGLFunctions_4_1_Core* f, + const GLuint program, + const GLuint vert, + const GLuint frag); + +// Create a GL shader program with a vertex and optional fragment shader. +void create_shader_program( + QOpenGLFunctions_4_1_Core* f, + GLuint& program, + const QByteArray* vert_source, + const QByteArray* frag_source); + +// Load a GLSL shader from file into a QByteArray. +QByteArray load_gl_shader(const QString& base_name); + +} // namespace studio +} // namespace appleseed + diff --git a/src/appleseed.studio/utility/miscellaneous.cpp b/src/appleseed.studio/utility/miscellaneous.cpp index af29d1b525..f26be7f75b 100644 --- a/src/appleseed.studio/utility/miscellaneous.cpp +++ b/src/appleseed.studio/utility/miscellaneous.cpp @@ -197,16 +197,6 @@ bool file_exists(const QString& path) return info.exists() && info.isFile(); } -QByteArray load_gl_shader(const QString& base_name) -{ - const QString resource_path(QString(":/shaders/%1").arg(base_name)); - - QFile file(resource_path); - file.open(QFile::ReadOnly); - - return file.readAll(); -} - QIcon load_icons(const QString& base_name) { const QString base_icon_filepath(make_app_path("icons/%1.png").arg(base_name)); diff --git a/src/appleseed.studio/utility/miscellaneous.h b/src/appleseed.studio/utility/miscellaneous.h index 2558cb9c13..361eadafef 100644 --- a/src/appleseed.studio/utility/miscellaneous.h +++ b/src/appleseed.studio/utility/miscellaneous.h @@ -80,9 +80,6 @@ QString combine_name_and_shortcut(const QString& name, const QKeySequence& short // Check whether a file exists. bool file_exists(const QString& path); -// Load a GLSL shader from file into a QByteArray. -QByteArray load_gl_shader(const QString& base_name); - // Load an icon and its variants (hover, disabled...) from the application's icons directory. QIcon load_icons(const QString& base_name);