diff --git a/src/app.cpp b/src/app.cpp index 8f813a0..e5adf56 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -53,14 +53,12 @@ void App::open_document(std::string path) // on complete if (success) { - async_start(); title_update(); for (int layer_index = 0; layer_index < canvas->m_canvas->m_layers.size(); layer_index++) { auto l = layers->add_layer(canvas->m_canvas->m_layers[layer_index]->m_name.c_str(), false); l->m_visibility->set_value(canvas->m_canvas->m_layers[layer_index]->m_visible); } - async_end(); } else { @@ -80,7 +78,6 @@ bool App::request_close() return true; if (!dialog_already_opened) { - async_start(); auto* m = layout[main_id]->add_child(); m->m_title->set_text("Unsaved document"); m->m_message->set_text("Do you want to close without saving?"); @@ -100,8 +97,6 @@ bool App::request_close() m->destroy(); dialog_already_opened = false; }; - async_redraw(); - async_end(); dialog_already_opened = true; } return false; @@ -109,6 +104,7 @@ bool App::request_close() void App::clear() { + assert(is_render_thread()); glClearColor(.1f, .1f, .1f, 1.f); glClear(GL_COLOR_BUFFER_BIT); } @@ -384,67 +380,75 @@ void App::upload(std::string filename, std::string name, std::function colors = { + { GL_DEBUG_SEVERITY_NOTIFICATION, 8 }, + { GL_DEBUG_SEVERITY_LOW, 8 }, + { GL_DEBUG_SEVERITY_MEDIUM, FOREGROUND_GREEN | FOREGROUND_INTENSITY }, + { GL_DEBUG_SEVERITY_HIGH, FOREGROUND_RED | FOREGROUND_INTENSITY }, + }; + if (severity == GL_DEBUG_SEVERITY_HIGH || severity == GL_DEBUG_SEVERITY_MEDIUM || severity == GL_DEBUG_SEVERITY_LOW) + { + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), colors[severity]); + LOG("OPENGL: %.*s", length, message); + FlushConsoleInputBuffer(GetStdHandle(STD_OUTPUT_HANDLE)); + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), info.wAttributes); +#ifdef _DEBUG + if (severity == GL_DEBUG_SEVERITY_HIGH) + __debugbreak(); +#endif + } +} +#endif + void App::init() { #ifdef _WIN32 if (glDebugMessageCallback) { - static CONSOLE_SCREEN_BUFFER_INFO info; - GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info); // colors: http://stackoverflow.com/questions/4053837/colorizing-text-in-the-console-with-c - glDebugMessageCallback([](GLenum source, GLenum type, GLuint id, - GLenum severity, GLsizei length, const GLchar* message, const void* userParam) + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info); + + render_task([] { - static std::map colors = { - { GL_DEBUG_SEVERITY_NOTIFICATION, 8 }, - { GL_DEBUG_SEVERITY_LOW, 8 }, - { GL_DEBUG_SEVERITY_MEDIUM, FOREGROUND_GREEN | FOREGROUND_INTENSITY }, - { GL_DEBUG_SEVERITY_HIGH, FOREGROUND_RED | FOREGROUND_INTENSITY }, - }; - if (severity == GL_DEBUG_SEVERITY_HIGH || severity == GL_DEBUG_SEVERITY_MEDIUM || severity == GL_DEBUG_SEVERITY_LOW) - { - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), colors[severity]); - LOG("OPENGL: %.*s", length, message); - FlushConsoleInputBuffer(GetStdHandle(STD_OUTPUT_HANDLE)); - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), info.wAttributes); - #ifdef _WIN32 && _DEBUG - if (severity == GL_DEBUG_SEVERITY_HIGH) - __debugbreak(); - #endif - } - }, nullptr); - glEnable(GL_DEBUG_OUTPUT); - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glDebugMessageCallback(handle_gl_callback, nullptr); + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + }); } #endif - LOG("GL version: %s", glGetString(GL_VERSION)); - LOG("GL vendor: %s", glGetString(GL_VENDOR)); - LOG("GL renderer: %s", glGetString(GL_RENDERER)); - - //GLint n_exts; - //glGetIntegerv(GL_NUM_EXTENSIONS, &n_exts); - //for (int i = 0; i < n_exts; i++) - //{ - // std::string ext = (const char*)glGetStringi(GL_EXTENSIONS, i); - // //if (ext.find("debug") != std::string::npos) - // { - // LOG("%s", glGetStringi(GL_EXTENSIONS, i)); - // } - //} - LOG("Screen Resolution: %dx%d", (int)width, (int)height); - //zoom = ceilf(width / 2000.f); - //zoom = 2; + render_task([] + { + LOG("GL version: %s", glGetString(GL_VERSION)); + LOG("GL vendor: %s", glGetString(GL_VENDOR)); + LOG("GL renderer: %s", glGetString(GL_RENDERER)); + + //GLint n_exts; + //glGetIntegerv(GL_NUM_EXTENSIONS, &n_exts); + //for (int i = 0; i < n_exts; i++) + //{ + // std::string ext = (const char*)glGetStringi(GL_EXTENSIONS, i); + // //if (ext.find("debug") != std::string::npos) + // { + // LOG("%s", glGetStringi(GL_EXTENSIONS, i)); + // } + //} - glDisable(GL_DEPTH_TEST); -#if defined(_WIN32) || defined(__OSX__) - glEnable(GL_PROGRAM_POINT_SIZE); - glEnable(GL_LINE_SMOOTH); -#endif - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBlendEquationSeparate(GL_FUNC_ADD, GL_MAX); + glDisable(GL_DEPTH_TEST); + #if defined(_WIN32) || defined(__OSX__) + glEnable(GL_PROGRAM_POINT_SIZE); + glEnable(GL_LINE_SMOOTH); + #endif + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendEquationSeparate(GL_FUNC_ADD, GL_MAX); + }); int run_counter = Settings::value("run_counter") + 1; Settings::set("run_counter", Serializer::Integer(run_counter)); @@ -462,15 +466,6 @@ void App::init() { message_box("License", "Could not validate this license, running in demo mode."); } - - //GLfloat width_range[2]; - //glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, width_range); - //LOG("GL line range: %f - %f", width_range[0], width_range[1]); - LOG("Screen Size: %f %f", width, height); - - GLint fb0; - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fb0); - LOG("Default Framebuffer %d", fb0); } void App::async_start() @@ -700,7 +695,6 @@ void App::terminate() NodeStrokePreview::terminate_renderer(); rec_stop(); - async_start(); TextureManager::invalidate(); ShaderManager::invalidate(); layout.unload(); @@ -716,7 +710,6 @@ void App::terminate() floating_layers.reset(); floating_picker.reset(); quick_mode_state.clear(); - async_end(); } void App::update_memory_usage(size_t bytes) diff --git a/src/app.h b/src/app.h index 1db3aef..56ce4f3 100644 --- a/src/app.h +++ b/src/app.h @@ -249,4 +249,69 @@ public: bool get_ui_rtl() const; void cmd_convert(std::string pano_path, std::string out_path); + + bool is_render_thread() + { + extern std::thread::id render_thread_id; + extern std::thread::id gl_thread; + return std::this_thread::get_id() == render_thread_id || std::this_thread::get_id() == gl_thread; + } + + // don't capture a reference to this ptr as the object may be destroyed + // by the time the task is executed + template::type> + std::future render_task_async(T task) + { +#ifdef _WIN32 + extern std::deque> render_tasklist; + extern std::mutex render_task_mutex; + extern std::condition_variable render_cv; + std::packaged_task pt(task); + std::future f = pt.get_future(); + if (is_render_thread()) + { + pt(); + } + else + { + { + std::lock_guard lock(render_task_mutex); + render_tasklist.push_back(std::move(pt)); + } + render_cv.notify_all(); + } + return f; +#endif // _WIN32 + } + + template::type> + R render_task(T task) + { +#ifdef _WIN32 + extern std::deque> render_tasklist; + extern std::mutex render_task_mutex; + extern std::condition_variable render_cv; + std::packaged_task pt(task); + std::future f = pt.get_future(); + if (is_render_thread()) + { + pt(); + } + else + { + { + std::lock_guard lock(render_task_mutex); + render_tasklist.push_back(std::move(pt)); + } + render_cv.notify_all(); + } + return f.get(); +#endif // _WIN32 + } + + void render_sync() + { + render_task([] {}); + } + }; diff --git a/src/app_dialogs.cpp b/src/app_dialogs.cpp index b6745b6..30574d4 100644 --- a/src/app_dialogs.cpp +++ b/src/app_dialogs.cpp @@ -28,7 +28,6 @@ std::shared_ptr App::show_progress(const std::string& title) NodeMessageBox* App::message_box(const std::string &title, const std::string& text, bool cancel_button) { - async_start(); auto* m = layout[main_id]->add_child(); m->m_title->set_text(title.c_str()); m->m_message->set_text(text.c_str()); @@ -36,8 +35,6 @@ NodeMessageBox* App::message_box(const std::string &title, const std::string& te if (!cancel_button) m->btn_cancel->destroy(); layout[main_id]->update(); - async_redraw(); - async_end(); return m; } @@ -80,7 +77,6 @@ void App::dialog_about() void App::dialog_newdoc() { auto show_dialog = [this] { - async_start(); auto dialog = std::make_shared(); dialog->m_manager = &layout; dialog->init(); @@ -153,7 +149,6 @@ void App::dialog_newdoc() dialog->destroy(); App::I.hideKeyboard(); }; - async_end(); }; if (canvas) @@ -190,7 +185,6 @@ void App::dialog_newdoc() void App::dialog_open() { auto show_dialog = [this] { - async_start(); // load thumbnail test auto dialog = std::make_shared(); dialog->m_manager = &layout; @@ -217,7 +211,6 @@ void App::dialog_open() // dialog->destroy(); // ActionManager::clear(); }; - async_end(); }; if (canvas) @@ -253,7 +246,6 @@ void App::dialog_open() void App::dialog_browse() { auto show_dialog = [this] { - async_start(); // load thumbnail test auto dialog = std::make_shared(); dialog->m_manager = &layout; @@ -277,7 +269,6 @@ void App::dialog_browse() dialog->destroy(); } }; - async_end(); }; if (canvas) diff --git a/src/app_shaders.cpp b/src/app_shaders.cpp index 8fa5d45..fe1b93a 100644 --- a/src/app_shaders.cpp +++ b/src/app_shaders.cpp @@ -9,16 +9,18 @@ void App::initShaders() std::logic_error("check_uniform_uniqueness() failed"); #endif // _DEBUG - GLint n_exts; - glGetIntegerv(GL_NUM_EXTENSIONS, &n_exts); - for (int i = 0; i < n_exts; i++) - { - std::string ext = (const char*)glGetStringi(GL_EXTENSIONS, i); - if (ext.find("shader_framebuffer_fetch") != std::string::npos) + render_task([] { + GLint n_exts; + glGetIntegerv(GL_NUM_EXTENSIONS, &n_exts); + for (int i = 0; i < n_exts; i++) { - ShaderManager::ext_framebuffer_fetch = true; + std::string ext = (const char*)glGetStringi(GL_EXTENSIONS, i); + if (ext.find("shader_framebuffer_fetch") != std::string::npos) + { + ShaderManager::ext_framebuffer_fetch = true; + } } - } + }); LOG("Shader Extension shader_framebuffer_fetch: %s", ShaderManager::ext_framebuffer_fetch ? "enabled" : "disabled"); diff --git a/src/brush.cpp b/src/brush.cpp index 686a888..4ae3c60 100644 --- a/src/brush.cpp +++ b/src/brush.cpp @@ -6,6 +6,7 @@ void BrushMesh::draw(const std::vector& samples, const glm::mat4& proj) { + assert(App::I.is_render_thread()); std::vector attributes; attributes.reserve(samples.size()); for (const auto& s : samples) @@ -69,64 +70,74 @@ void BrushMesh::draw(const std::vector& samples, const glm::mat4& } bool BrushMesh::create() { - static GLushort idx[6]{ 0, 1, 2, 0, 2, 3 }; - static vertex_t vertices[4]{ - { { -.5f, -.5f, 0, 1 }, { 0, 0 } }, // A B----C - { { -.5f, .5f, 0, 1 }, { 0, 1 } }, // B --\ | | - { { .5f, .5f, 0, 1 }, { 1, 1 } }, // C --/ | | - { { .5f, -.5f, 0, 1 }, { 1, 0 } }, // D A----D - }; - glGenBuffers(3, buffers); - if (!(buffers[0] && buffers[1] && buffers[2])) - return false; + bool ret = true; + App::I.render_task([&] + { + static GLushort idx[6]{ 0, 1, 2, 0, 2, 3 }; + static vertex_t vertices[4]{ + { { -.5f, -.5f, 0, 1 }, { 0, 0 } }, // A B----C + { { -.5f, .5f, 0, 1 }, { 0, 1 } }, // B --\ | | + { { .5f, .5f, 0, 1 }, { 1, 1 } }, // C --/ | | + { { .5f, -.5f, 0, 1 }, { 1, 0 } }, // D A----D + }; + glGenBuffers(3, buffers); + if (!(buffers[0] && buffers[1] && buffers[2])) + { + ret = false; + return; + } - static instance_t inst{ glm::mat4(1), .1f }; - glBindBuffer(GL_ARRAY_BUFFER, buffers[2]); - glBufferData(GL_ARRAY_BUFFER, sizeof(instance_t), &inst, GL_STATIC_DRAW); + static instance_t inst{ glm::mat4(1), .1f }; + glBindBuffer(GL_ARRAY_BUFFER, buffers[2]); + glBufferData(GL_ARRAY_BUFFER, sizeof(instance_t), &inst, GL_STATIC_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(idx), idx, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(idx), idx, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); - auto shader = ShaderManager::get(kShader::BrushStroke); + auto shader = ShaderManager::get(kShader::BrushStroke); - loc_flow = shader->GetAttribLocation("a_flow"); - loc_mvp = shader->GetAttribLocation("a_mvp"); + loc_flow = shader->GetAttribLocation("a_flow"); + loc_mvp = shader->GetAttribLocation("a_mvp"); #if USE_VBO - glGenVertexArrays(1, &vao); - if (!vao) - return false; - glBindVertexArray(vao); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); - glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, pos)); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs)); - glBindBuffer(GL_ARRAY_BUFFER, buffers[2]); - // Loop over each column of the matrix... - for (int i = 0; i < 4; i++) - { - // Set up the vertex attribute - glVertexAttribPointer(loc_mvp + i, 4, GL_FLOAT, GL_FALSE, sizeof(instance_t), - (GLvoid*)(offsetof(instance_t, mvp) + sizeof(glm::vec4) * i)); - // Enable it - glEnableVertexAttribArray(loc_mvp + i); - // Make it instanced - glVertexAttribDivisor(loc_mvp + i, 1); - } - glEnableVertexAttribArray(loc_flow); - glVertexAttribPointer(loc_flow, 1, GL_FLOAT, GL_FALSE, sizeof(instance_t), - (GLvoid*)offsetof(instance_t, flow)); - glVertexAttribDivisor(loc_flow, 1); - glBindVertexArray(0); + glGenVertexArrays(1, &vao); + if (!vao) + { + ret = false; + return; + } + glBindVertexArray(vao); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, pos)); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs)); + glBindBuffer(GL_ARRAY_BUFFER, buffers[2]); + // Loop over each column of the matrix... + for (int i = 0; i < 4; i++) + { + // Set up the vertex attribute + glVertexAttribPointer(loc_mvp + i, 4, GL_FLOAT, GL_FALSE, sizeof(instance_t), + (GLvoid*)(offsetof(instance_t, mvp) + sizeof(glm::vec4) * i)); + // Enable it + glEnableVertexAttribArray(loc_mvp + i); + // Make it instanced + glVertexAttribDivisor(loc_mvp + i, 1); + } + glEnableVertexAttribArray(loc_flow); + glVertexAttribPointer(loc_flow, 1, GL_FLOAT, GL_FALSE, sizeof(instance_t), + (GLvoid*)offsetof(instance_t, flow)); + glVertexAttribDivisor(loc_flow, 1); + glBindVertexArray(0); #endif + }); - return true; + return ret; } StrokeSample Stroke::randomize_sample(const glm::vec3& pos, float pressure, float dir_angle) { diff --git a/src/canvas.cpp b/src/canvas.cpp index 660df80..ad255ca 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -1474,10 +1474,13 @@ void Canvas::FloodData::apply() if (!dirty[plane]) continue; auto& rtt = layer->m_rtt[plane]; - rtt.bindTexture(); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rtt.getWidth(), rtt.getHeight(), - GL_RGBA, GL_UNSIGNED_BYTE, rgb[plane].get()); - rtt.unbindTexture(); + App::I.render_task([&] + { + rtt.bindTexture(); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rtt.getWidth(), rtt.getHeight(), + GL_RGBA, GL_UNSIGNED_BYTE, rgb[plane].get()); + rtt.unbindTexture(); + }); layer->m_dirty_face[plane] = true; } } @@ -2766,111 +2769,115 @@ bool Canvas::project_open_thread(std::string file_path) Image Canvas::thumbnail_generate(int w, int h) { - // save viewport and clear color states - GLint vp[4]; - GLfloat cc[4]; - glGetIntegerv(GL_VIEWPORT, vp); - glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); - GLboolean blend = glIsEnabled(GL_BLEND); + Image image; + image.create(w, h); - // prepare common states - glViewport(0, 0, w, h); - - RTT fb; - fb.create(w, h); - fb.bindFramebuffer(); - Plane m_face_plane; - m_face_plane.create<1>(2, 2); - Texture2D blendtex; - blendtex.create(w, h); - - // recalculate because of different aspect ratio than the m_proj matrix - glm::mat4 proj = glm::perspective(glm::radians(m_cam_fov), (float)w / (float)h, 0.1f, 1000.f); - - fb.clear({ 1, 1, 1, 0 }); - for (int i = 0; i < 6; i++) + App::I.render_task([this, w, h, &image] { - glDisable(GL_BLEND); - auto plane_mvp = proj * m_mv * m_plane_transform[i] * glm::translate(glm::vec3(0, 0, -1)); + // save viewport and clear color states + GLint vp[4]; + GLfloat cc[4]; + glGetIntegerv(GL_VIEWPORT, vp); + glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); + GLboolean blend = glIsEnabled(GL_BLEND); - ShaderManager::use(kShader::TextureBlend); - ShaderManager::u_int(kShaderUniform::Tex, 0); - ShaderManager::u_int(kShaderUniform::TexA, 1); - ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp); - if (!ShaderManager::ext_framebuffer_fetch) + // prepare common states + glViewport(0, 0, w, h); + + RTT fb; + fb.create(w, h); + fb.bindFramebuffer(); + Plane m_face_plane; + m_face_plane.create<1>(2, 2); + Texture2D blendtex; + blendtex.create(w, h); + + // recalculate because of different aspect ratio than the m_proj matrix + glm::mat4 proj = glm::perspective(glm::radians(m_cam_fov), (float)w / (float)h, 0.1f, 1000.f); + + fb.clear({ 1, 1, 1, 0 }); + for (int i = 0; i < 6; i++) { - ShaderManager::u_int(kShaderUniform::TexBG, 2); - glActiveTexture(GL_TEXTURE2); - blendtex.bind(); - m_sampler_bg.bind(2); - } - m_sampler_bg.bind(0); // nearest - m_sampler_mask.bind(1); // linear - for (int layer_index = 0; layer_index < m_layers.size(); layer_index++) - { - if (!m_layers[layer_index]->m_visible || - m_layers[layer_index]->m_opacity == 0.f || - !m_layers[layer_index]->m_dirty_face[i]) - continue; + glDisable(GL_BLEND); + auto plane_mvp = proj * m_mv * m_plane_transform[i] * glm::translate(glm::vec3(0, 0, -1)); + + ShaderManager::use(kShader::TextureBlend); + ShaderManager::u_int(kShaderUniform::Tex, 0); + ShaderManager::u_int(kShaderUniform::TexA, 1); + ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp); + if (!ShaderManager::ext_framebuffer_fetch) + { + ShaderManager::u_int(kShaderUniform::TexBG, 2); + glActiveTexture(GL_TEXTURE2); + blendtex.bind(); + m_sampler_bg.bind(2); + } + m_sampler_bg.bind(0); // nearest + m_sampler_mask.bind(1); // linear + for (int layer_index = 0; layer_index < m_layers.size(); layer_index++) + { + if (!m_layers[layer_index]->m_visible || + m_layers[layer_index]->m_opacity == 0.f || + !m_layers[layer_index]->m_dirty_face[i]) + continue; + if (!ShaderManager::ext_framebuffer_fetch) + { + glActiveTexture(GL_TEXTURE2); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, w, h); + } + ShaderManager::u_int(kShaderUniform::BlendMode, m_layers[layer_index]->m_blend_mode); + ShaderManager::u_float(kShaderUniform::Alpha, m_layers[layer_index]->m_opacity); + glActiveTexture(GL_TEXTURE0); + m_layers[layer_index]->m_rtt[i].bindTexture(); + glActiveTexture(GL_TEXTURE1); + m_layers[layer_index]->m_rtt[i].bindTexture(); + m_face_plane.draw_fill(); + m_layers[layer_index]->m_rtt[i].unbindTexture(); + glActiveTexture(GL_TEXTURE0); + m_layers[layer_index]->m_rtt[i].unbindTexture(); + } + if (!ShaderManager::ext_framebuffer_fetch) { glActiveTexture(GL_TEXTURE2); - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, w, h); + blendtex.unbind(); } - ShaderManager::u_int(kShaderUniform::BlendMode, m_layers[layer_index]->m_blend_mode); - ShaderManager::u_float(kShaderUniform::Alpha, m_layers[layer_index]->m_opacity); - glActiveTexture(GL_TEXTURE0); - m_layers[layer_index]->m_rtt[i].bindTexture(); - glActiveTexture(GL_TEXTURE1); - m_layers[layer_index]->m_rtt[i].bindTexture(); - m_face_plane.draw_fill(); - m_layers[layer_index]->m_rtt[i].unbindTexture(); - glActiveTexture(GL_TEXTURE0); - m_layers[layer_index]->m_rtt[i].unbindTexture(); - } - if (!ShaderManager::ext_framebuffer_fetch) - { - glActiveTexture(GL_TEXTURE2); + glActiveTexture(GL_TEXTURE0); + blendtex.bind(); + // copy the content of the fb before drawing the grid + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, w, h); + + // draw the grid + ShaderManager::use(kShader::Checkerboard); + ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp); + m_face_plane.draw_fill(); + + // now blend with the background + glEnable(GL_BLEND); + ShaderManager::use(kShader::Texture); + ShaderManager::u_int(kShaderUniform::Tex, 0); + ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f)); + m_sampler_mask.bind(0); // linear + m_plane.draw_fill(); + blendtex.unbind(); } + fb.unbindFramebuffer(); + + // read the rendered image + fb.readTextureData((uint8_t*)image.data()); + + fb.destroy(); + blendtex.destroy(); + + // restore viewport and clear color states + blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND); + glViewport(vp[0], vp[1], vp[2], vp[3]); + glClearColor(cc[0], cc[1], cc[2], cc[3]); glActiveTexture(GL_TEXTURE0); - blendtex.bind(); - // copy the content of the fb before drawing the grid - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, w, h); - - // draw the grid - ShaderManager::use(kShader::Checkerboard); - ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp); - m_face_plane.draw_fill(); - - // now blend with the background - glEnable(GL_BLEND); - ShaderManager::use(kShader::Texture); - ShaderManager::u_int(kShaderUniform::Tex, 0); - ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f)); - m_sampler_mask.bind(0); // linear - m_plane.draw_fill(); - - blendtex.unbind(); - } - - fb.unbindFramebuffer(); - - // read the rendered image - Image image; - image.create(w, h); - fb.readTextureData((uint8_t*)image.data()); - - fb.destroy(); - blendtex.destroy(); - - // restore viewport and clear color states - blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND); - glViewport(vp[0], vp[1], vp[2], vp[3]); - glClearColor(cc[0], cc[1], cc[2], cc[3]); - glActiveTexture(GL_TEXTURE0); + }); return image; } @@ -2904,140 +2911,146 @@ Image Canvas::thumbnail_read(std::string file_path) void Canvas::draw_objects_direct(std::function observer, Layer& layer) { - // save viewport and clear color states - GLint vp[4]; - GLfloat cc[4]; - glGetIntegerv(GL_VIEWPORT, vp); - glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); - GLboolean blend = glIsEnabled(GL_BLEND); - - // prepare common states - glViewport(0, 0, layer.w, layer.h); - glDisable(GL_BLEND); - - GLuint rboID; - glGenRenderbuffers(1, &rboID); - glBindRenderbuffer(GL_RENDERBUFFER, rboID); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, layer.w, layer.h); - glBindRenderbuffer(GL_RENDERBUFFER, 0); - - glm::mat4 proj = glm::perspective(glm::radians(90.f), 1.f, .01f, 1000.f); - for (int i = 0; i < 6; i++) + App::I.render_task([&] { - glm::mat4 plane_camera = glm::lookAt(glm::vec3(0), m_plane_origin[i], m_plane_tangent[i]); - layer.m_rtt[i].bindFramebuffer(); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboID); + // save viewport and clear color states + GLint vp[4]; + GLfloat cc[4]; + glGetIntegerv(GL_VIEWPORT, vp); + glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); + GLboolean blend = glIsEnabled(GL_BLEND); - observer(plane_camera, proj, i); + // prepare common states + glViewport(0, 0, layer.w, layer.h); + glDisable(GL_BLEND); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); - layer.m_rtt[i].unbindFramebuffer(); + GLuint rboID; + glGenRenderbuffers(1, &rboID); + glBindRenderbuffer(GL_RENDERBUFFER, rboID); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, layer.w, layer.h); + glBindRenderbuffer(GL_RENDERBUFFER, 0); - layer.m_dirty_face[i] = true; - layer.m_dirty_box[i] = { 0, 0, layer.w, layer.h }; - } + glm::mat4 proj = glm::perspective(glm::radians(90.f), 1.f, .01f, 1000.f); + for (int i = 0; i < 6; i++) + { + glm::mat4 plane_camera = glm::lookAt(glm::vec3(0), m_plane_origin[i], m_plane_tangent[i]); + layer.m_rtt[i].bindFramebuffer(); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboID); - glDeleteRenderbuffers(1, &rboID); + observer(plane_camera, proj, i); - // restore viewport and clear color states - blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND); - glViewport(vp[0], vp[1], vp[2], vp[3]); - glClearColor(cc[0], cc[1], cc[2], cc[3]); - glActiveTexture(GL_TEXTURE0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); + layer.m_rtt[i].unbindFramebuffer(); - draw_merge(); + layer.m_dirty_face[i] = true; + layer.m_dirty_box[i] = { 0, 0, layer.w, layer.h }; + } + + glDeleteRenderbuffers(1, &rboID); + + // restore viewport and clear color states + blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND); + glViewport(vp[0], vp[1], vp[2], vp[3]); + glClearColor(cc[0], cc[1], cc[2], cc[3]); + glActiveTexture(GL_TEXTURE0); + + draw_merge(); + }); } void Canvas::draw_objects(std::function observer, Layer& layer) { - // save viewport and clear color states - GLint vp[4]; - GLfloat cc[4]; - glGetIntegerv(GL_VIEWPORT, vp); - glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); - GLboolean blend = glIsEnabled(GL_BLEND); - - // prepare common states - glViewport(0, 0, layer.w, layer.h); - glDisable(GL_BLEND); - - GLuint rboID; - glGenRenderbuffers(1, &rboID); - glBindRenderbuffer(GL_RENDERBUFFER, rboID); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, layer.w, layer.h); - glBindRenderbuffer(GL_RENDERBUFFER, 0); - - RTT rtt; - rtt.create(layer.w, layer.h); - rtt.bindFramebuffer(); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboID); - rtt.unbindFramebuffer(); - - // allocate action to add to history - auto action = new ActionStroke; - action->was_saved = !m_unsaved; - - glm::mat4 proj = glm::perspective(glm::radians(90.f), 1.f, .01f, 1000.f); - for (int i = 0; i < 6; i++) + App::I.render_task([&] { - glm::mat4 plane_camera = glm::lookAt(glm::vec3(0), m_plane_origin[i], m_plane_tangent[i]); + // save viewport and clear color states + GLint vp[4]; + GLfloat cc[4]; + glGetIntegerv(GL_VIEWPORT, vp); + glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); + GLboolean blend = glIsEnabled(GL_BLEND); + + // prepare common states + glViewport(0, 0, layer.w, layer.h); + glDisable(GL_BLEND); + + GLuint rboID; + glGenRenderbuffers(1, &rboID); + glBindRenderbuffer(GL_RENDERBUFFER, rboID); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, layer.w, layer.h); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + RTT rtt; + rtt.create(layer.w, layer.h); rtt.bindFramebuffer(); - rtt.clear({ 1, 1, 1, 0 }); - observer(plane_camera, proj, i); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboID); rtt.unbindFramebuffer(); - glm::vec4 bounds = rtt.calc_bounds(); + // allocate action to add to history + auto action = new ActionStroke; + action->was_saved = !m_unsaved; - layer.m_rtt[i].bindFramebuffer(); - - // save image before commit - glm::vec2 box_sz = zw(bounds) - xy(bounds); - bool has_data = box_sz.x > 0 && box_sz.y > 0; - if (has_data) + glm::mat4 proj = glm::perspective(glm::radians(90.f), 1.f, .01f, 1000.f); + for (int i = 0; i < 6; i++) { - action->m_image[i] = std::make_unique(box_sz.x * box_sz.y * 4); - glReadPixels(bounds.x, bounds.y, box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, action->m_image[i].get()); - action->m_box[i] = bounds; + glm::mat4 plane_camera = glm::lookAt(glm::vec3(0), m_plane_origin[i], m_plane_tangent[i]); + rtt.bindFramebuffer(); + rtt.clear({ 1, 1, 1, 0 }); + observer(plane_camera, proj, i); + rtt.unbindFramebuffer(); + + glm::vec4 bounds = rtt.calc_bounds(); + + layer.m_rtt[i].bindFramebuffer(); + + // save image before commit + glm::vec2 box_sz = zw(bounds) - xy(bounds); + bool has_data = box_sz.x > 0 && box_sz.y > 0; + if (has_data) + { + action->m_image[i] = std::make_unique(box_sz.x * box_sz.y * 4); + glReadPixels(bounds.x, bounds.y, box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, action->m_image[i].get()); + action->m_box[i] = bounds; + } + + action->m_old_box[i] = layer.m_dirty_box[i]; + action->m_old_dirty[i] = layer.m_dirty_face[i]; + + // draw the tmp layer into the actual layer + if (has_data) + { + ShaderManager::use(kShader::Texture); + ShaderManager::u_int(kShaderUniform::Tex, 0); + ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-0.5f, 0.5f, -0.5f, 0.5f)); + glActiveTexture(GL_TEXTURE0); + m_sampler_nearest.bind(0); + rtt.bindTexture(); + m_plane.draw_fill(); + rtt.unbindTexture(); + + layer.m_dirty_face[i] = true; + layer.m_dirty_box[i] = { glm::min(xy(layer.m_dirty_box[i]), xy(bounds)), glm::max(zw(layer.m_dirty_box[i]), zw(bounds)) }; + } + + layer.m_rtt[i].unbindFramebuffer(); } - action->m_old_box[i] = layer.m_dirty_box[i]; - action->m_old_dirty[i] = layer.m_dirty_face[i]; + // save history + action->m_layer_idx = m_current_layer_idx; + action->m_canvas = this; + //action->m_stroke = std::move(m_current_stroke); + ActionManager::add(action); - // draw the tmp layer into the actual layer - if (has_data) - { - ShaderManager::use(kShader::Texture); - ShaderManager::u_int(kShaderUniform::Tex, 0); - ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-0.5f, 0.5f, -0.5f, 0.5f)); - glActiveTexture(GL_TEXTURE0); - m_sampler_nearest.bind(0); - rtt.bindTexture(); - m_plane.draw_fill(); - rtt.unbindTexture(); - - layer.m_dirty_face[i] = true; - layer.m_dirty_box[i] = { glm::min(xy(layer.m_dirty_box[i]), xy(bounds)), glm::max(zw(layer.m_dirty_box[i]), zw(bounds)) }; - } + glDeleteRenderbuffers(1, &rboID); + rtt.destroy(); - layer.m_rtt[i].unbindFramebuffer(); - } + // restore viewport and clear color states + blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND); + glViewport(vp[0], vp[1], vp[2], vp[3]); + glClearColor(cc[0], cc[1], cc[2], cc[3]); + glActiveTexture(GL_TEXTURE0); - // save history - action->m_layer_idx = m_current_layer_idx; - action->m_canvas = this; - //action->m_stroke = std::move(m_current_stroke); - ActionManager::add(action); - - glDeleteRenderbuffers(1, &rboID); - rtt.destroy(); - - // restore viewport and clear color states - blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND); - glViewport(vp[0], vp[1], vp[2], vp[3]); - glClearColor(cc[0], cc[1], cc[2], cc[3]); - glActiveTexture(GL_TEXTURE0); - - draw_merge(); + draw_merge(); + }); } void Canvas::draw_objects(std::function observer) @@ -3153,7 +3166,6 @@ void Layer::destroy() void Layer::optimize() { int saved_bytes = 0; - glBindTexture(GL_TEXTURE_2D, 0); for (int i = 0; i < 6; i++) { if (!m_dirty_face[i]) @@ -3209,24 +3221,25 @@ void Layer::restore(const Snapshot& snap) // it's just a quick fix DON'T SHIP!! //m_rtt[i].recreate(); - m_rtt[i].bindTexture(); - glm::vec2 box_sz = zw(m_dirty_box[i]) - xy(m_dirty_box[i]); - glTexSubImage2D(GL_TEXTURE_2D, 0, - m_dirty_box[i].x, m_dirty_box[i].y, - box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, - snap.image[i].get()); - m_rtt[i].unbindTexture(); - LOG("restore face %d - %d bytes (%dx%d)", i, - (int)box_sz.x * (int)box_sz.y * 4, (int)box_sz.x, (int)box_sz.y); + App::I.render_task_async([this,i,&snap] + { + m_rtt[i].bindTexture(); + glm::vec2 box_sz = zw(m_dirty_box[i]) - xy(m_dirty_box[i]); + glTexSubImage2D(GL_TEXTURE_2D, 0, + m_dirty_box[i].x, m_dirty_box[i].y, + box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, + snap.image[i].get()); + m_rtt[i].unbindTexture(); + LOG("restore face %d - %d bytes (%dx%d)", i, + (int)box_sz.x * (int)box_sz.y * 4, (int)box_sz.x, (int)box_sz.y); + }); } + App::I.render_sync(); } Layer::Snapshot Layer::snapshot(std::array * dirty_box /*= nullptr*/, std::array * dirty_face /*= nullptr*/) { Snapshot snap; - static int counter = 0; - glBindTexture(GL_TEXTURE_2D, 0); - //glBindFramebuffer(GL_FRAMEBUFFER, 0); for (int i = 0; i < 6; i++) { snap.m_dirty_box[i] = dirty_box ? dirty_box->at(i) : m_dirty_box[i]; @@ -3238,45 +3251,51 @@ Layer::Snapshot Layer::snapshot(std::array * dirty_box /*= nullptr snap.image[i] = std::make_unique(m_rtt[i].bytes()); //glReadBuffer(GL_BACK); - m_rtt[i].bindFramebuffer(); - glm::vec2 box_sz = zw(snap.m_dirty_box[i]) - xy(snap.m_dirty_box[i]); - glReadPixels(snap.m_dirty_box[i].x, snap.m_dirty_box[i].y, box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, snap.image[i].get()); - m_rtt[i].unbindFramebuffer(); + App::I.render_task_async([this,i,&snap] + { + m_rtt[i].bindFramebuffer(); + glm::vec2 box_sz = zw(snap.m_dirty_box[i]) - xy(snap.m_dirty_box[i]); + glReadPixels(snap.m_dirty_box[i].x, snap.m_dirty_box[i].y, box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, snap.image[i].get()); + m_rtt[i].unbindFramebuffer(); + }); //glReadBuffer(GL_NONE); } - counter++; + App::I.render_sync(); return snap; } void Layer::clear(const glm::vec4& c) { - // push clear color state - GLfloat cc[4]; - glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); - glClearColor(c.r, c.g, c.b, c.a); - - bool erase = (c.a == 0.f); - - for (int i = 0; i < 6; i++) + App::I.render_task([&] { - m_rtt[i].bindFramebuffer(); - glClear(GL_COLOR_BUFFER_BIT); - m_rtt[i].unbindFramebuffer(); + // push clear color state + GLfloat cc[4]; + glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); + glClearColor(c.r, c.g, c.b, c.a); - if (erase) - { - m_dirty_box[i] = glm::vec4(w, h, 0, 0); // reset bounding box - m_dirty_face[i] = false; - } - else - { - m_dirty_box[i] = glm::vec4(0, 0, w, h); // reset bounding box - m_dirty_face[i] = true; - } - } + bool erase = (c.a == 0.f); - // restore clear color state - glClearColor(cc[0], cc[1], cc[2], cc[3]); + for (int i = 0; i < 6; i++) + { + m_rtt[i].bindFramebuffer(); + glClear(GL_COLOR_BUFFER_BIT); + m_rtt[i].unbindFramebuffer(); + + if (erase) + { + m_dirty_box[i] = glm::vec4(w, h, 0, 0); // reset bounding box + m_dirty_face[i] = false; + } + else + { + m_dirty_box[i] = glm::vec4(0, 0, w, h); // reset bounding box + m_dirty_face[i] = true; + } + } + + // restore clear color state + glClearColor(cc[0], cc[1], cc[2], cc[3]); + }); } bool Layer::create(int width, int height, std::string name) @@ -3284,15 +3303,18 @@ bool Layer::create(int width, int height, std::string name) m_name = name; w = width; h = height; - for (int i = 0; i < 6; i++) + App::I.render_task([&] { - m_rtt[i].create(width, height); - m_rtt[i].bindFramebuffer(); - m_rtt[i].clear(); - m_rtt[i].unbindFramebuffer(); - m_dirty_box[i] = glm::vec4(w, h, 0, 0); // reset bounding box - m_dirty_face[i] = false; - } + for (int i = 0; i < 6; i++) + { + m_rtt[i].create(width, height); + m_rtt[i].bindFramebuffer(); + m_rtt[i].clear(); + m_rtt[i].unbindFramebuffer(); + m_dirty_box[i] = glm::vec4(w, h, 0, 0); // reset bounding box + m_dirty_face[i] = false; + } + }); return true; } diff --git a/src/canvas_modes.cpp b/src/canvas_modes.cpp index dd95e56..f7e33b4 100644 --- a/src/canvas_modes.cpp +++ b/src/canvas_modes.cpp @@ -213,6 +213,7 @@ void CanvasModePen::on_MouseEvent(MouseEvent* me, glm::vec2& loc) void CanvasModePen::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) { + assert(App::I.is_render_thread()); if (m_draw_tip) { const auto& brush = Canvas::I->m_current_brush; diff --git a/src/font.cpp b/src/font.cpp index 6e3f693..170cb72 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -3,6 +3,8 @@ #include "font.h" #include "shader.h" #include "asset.h" +#include "util.h" +#include "app.h" std::map FontManager::m_fonts; Sampler FontManager::m_sampler; @@ -58,18 +60,21 @@ const Font& FontManager::get(kFont id) bool TextMesh::create() { - glGenBuffers(2, font_buffers); + App::I.render_task([this] + { + glGenBuffers(2, font_buffers); #if USE_VBO - glGenVertexArrays(1, &font_array); - glBindVertexArray(font_array); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, font_buffers[1]); - glBindBuffer(GL_ARRAY_BUFFER, font_buffers[0]); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (GLvoid*)0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (GLvoid*)(sizeof(float)*2)); - glBindVertexArray(0); + glGenVertexArrays(1, &font_array); + glBindVertexArray(font_array); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, font_buffers[1]); + glBindBuffer(GL_ARRAY_BUFFER, font_buffers[0]); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (GLvoid*)0); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (GLvoid*)(sizeof(float) * 2)); + glBindVertexArray(0); #endif // USE_VBO + }); return true; } @@ -120,17 +125,21 @@ void TextMesh::update(kFont id, const char* text) vi -= glm::vec4(bbmin, 0, 0); bb = bbmax - bbmin; font_array_count = (int)idx.size(); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, font_buffers[1]); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx.size() * sizeof(GLushort), idx.data(), GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, font_buffers[0]); - glBufferData(GL_ARRAY_BUFFER, v.size() * sizeof(glm::vec4), v.data(), GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + App::I.render_task([&] + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, font_buffers[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx.size() * sizeof(GLushort), idx.data(), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, font_buffers[0]); + glBufferData(GL_ARRAY_BUFFER, v.size() * sizeof(glm::vec4), v.data(), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + }); } } void TextMesh::draw() { + assert(App::I.is_render_thread()); auto& f = FontManager::get(font_id); if (f.font_tex.ready()) { diff --git a/src/main.cpp b/src/main.cpp index e674315..a0dcdfc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -33,12 +33,19 @@ std::thread::id gl_thread; std::map vkey_map; std::thread hmd_renderer; -std::thread renderer; +std::thread ui_renderer; int vr_frames = 0; int running = -1; int vr_running = 0; -std::mutex render_mutex; +std::mutex ui_render_mutex; +std::condition_variable ui_render_cv; + +std::deque> render_tasklist; +std::mutex render_task_mutex; std::condition_variable render_cv; +std::thread render_thread; +std::thread::id render_thread_id; +bool render_running = false; int gl_count = 0; std::deque> tasklist; @@ -112,6 +119,63 @@ void destroy_window() }); } +bool is_render_thread() +{ + extern std::thread::id render_thread_id; + extern std::thread::id gl_thread; + return std::this_thread::get_id() == render_thread_id || std::this_thread::get_id() == gl_thread; +} + +template::type> +std::future render_task_async(T task) +{ +#ifdef _WIN32 + extern std::deque> render_tasklist; + extern std::mutex render_task_mutex; + extern std::condition_variable render_cv; + std::packaged_task pt(task); + std::future f = pt.get_future(); + if (is_render_thread()) + { + pt(); + } + else + { + { + std::lock_guard lock(render_task_mutex); + render_tasklist.push_back(std::move(pt)); + } + render_cv.notify_all(); + } + return f; +#endif // _WIN32 +} + +template::type> +R render_task(T task) +{ +#ifdef _WIN32 + extern std::deque> render_tasklist; + extern std::mutex render_task_mutex; + extern std::condition_variable render_cv; + std::packaged_task pt(task); + std::future f = pt.get_future(); + if (is_render_thread()) + { + pt(); + } + else + { + { + std::lock_guard lock(render_task_mutex); + render_tasklist.push_back(std::move(pt)); + } + render_cv.notify_all(); + } + return f.get(); +#endif // _WIN32 +} + void async_lock() { //std::lock_guard _lock(async_mutex); @@ -166,7 +230,7 @@ void async_unlock() void win32_render_thread_notify() { - render_cv.notify_all(); + ui_render_cv.notify_all(); } void win32_show_cursor(bool visible) @@ -257,12 +321,6 @@ std::string win32_open_dir() return Buffer; } -struct async_locker -{ - async_locker() { async_lock(); } - ~async_locker() { async_unlock(); } -}; - int read_WMI_info() { // see: http://win32easy.blogspot.co.uk/2011/03/wmi-in-c-query-everyting-from-your-os.html @@ -539,16 +597,13 @@ bool win32_vr_start() vive = new Vive; vive->on_draw = [](const glm::mat4& proj, const glm::mat4& view, const glm::mat4& pose) { App::I.vr_draw(proj, view, pose); }; - async_lock(); if (!vive->Initialize()) { delete vive; vive = nullptr; LOG("VR: failed to initialize vive"); - async_unlock(); return false; } - async_unlock(); hmd_renderer = std::thread([&] { if (!vive) @@ -726,6 +781,46 @@ BOOL UnadjustWindowRectEx(LPRECT prc, DWORD dwStyle, BOOL fMenu, DWORD dwExStyle return fRc; } +void render_thread_main() +{ + uint32_t count = 0; + render_thread_id = std::this_thread::get_id(); + render_running = true; + while (render_running == 1) + { + std::deque> working_list; + + // move the task list locally to free the queue for other threads + { + std::unique_lock lock(render_task_mutex); + render_cv.wait(lock, [] { return render_tasklist.empty() && render_running ? false : true; }); + working_list = std::move(render_tasklist); + } + + //{ + // std::lock_guard lock(task_mutex); + // working_list.insert(working_list.end(), + // std::make_move_iterator(tasklist.begin()), + // std::make_move_iterator(tasklist.end())); + // tasklist.clear(); + //} + + // execute the tasks + if (!working_list.empty()) + { + async_lock(); + while (!working_list.empty()) + { + //LOG("render task %d", count); + count++; + working_list.front()(); + working_list.pop_front(); + } + async_unlock(); + } + } +} + int main(int argc, char** argv) { WNDCLASS wc; @@ -917,7 +1012,10 @@ int main(int argc, char** argv) LOG("RegisterTouchWindow error: %s", GetLastErrorAsString().c_str()); } - async_lock(); + wglMakeCurrent(NULL, NULL); + + running = 1; + render_thread = std::thread(render_thread_main); LOG("init app"); App::I.init(); @@ -932,15 +1030,11 @@ int main(int argc, char** argv) LOG("SKIP init WinTab"); } - async_unlock(); - LOG("change icon"); SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON1))); - running = 1; - - renderer = std::thread([&] { + ui_renderer = std::thread([&] { BT_SetTerminate(); LOG("start render thread"); const float target_fps = 10; @@ -989,13 +1083,11 @@ int main(int argc, char** argv) if (!working_list.empty()) { - async_lock(); while (!working_list.empty()) { working_list.front()(); working_list.pop_front(); } - async_unlock(); //LOG("clear"); //WacomTablet::I.m_stylus = false; //WacomTablet::I.m_eraser = false; @@ -1016,7 +1108,7 @@ int main(int argc, char** argv) App::I.tick(dt); - std::unique_lock lock(render_mutex); + std::unique_lock lock(ui_render_mutex); if (render_timer > 1.0f / target_fps) { App::I.redraw = true; @@ -1041,19 +1133,20 @@ int main(int argc, char** argv) if (App::I.redraw) { - async_lock(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - App::I.clear(); - App::I.update(frame_timer); - SwapBuffers(hDC); - async_unlock(); + render_task([frame_timer] + { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + App::I.clear(); + App::I.update(frame_timer); + SwapBuffers(hDC); + }); frame_timer = 0; frames++; } const int framerate = (1.f / target_tick_rate) * 1000; const int diff = framerate - (t1 - t0); - render_cv.wait_for(lock, std::chrono::milliseconds(diff)); + //render_cv.wait_for(lock, std::chrono::milliseconds(diff)); //std::this_thread::sleep_for(std::chrono::milliseconds(30)); t0 = t1; } @@ -1109,10 +1202,11 @@ int main(int argc, char** argv) } if (!tasklist.empty()) - render_cv.notify_all(); + ui_render_cv.notify_all(); } // Clean up WacomTablet::I.terminate(); + render_cv.notify_all(); UnregisterClass(className, hInst); LogRemote::I.stop(); @@ -1132,13 +1226,16 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { case WM_USER_CLOSE: running = 0; - render_cv.notify_all(); - if (renderer.joinable()) - renderer.join(); + ui_render_cv.notify_all(); + if (ui_renderer.joinable()) + ui_renderer.join(); if (hmd_renderer.joinable()) hmd_renderer.join(); App::I.terminate(); PostQuitMessage(0); + render_running = false; + if (render_thread.joinable()) + render_thread.join(); return 0; case WM_PAINT: App::I.redraw = true; @@ -1150,13 +1247,16 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) if (App::I.request_close()) { running = 0; - render_cv.notify_all(); - if (renderer.joinable()) - renderer.join(); + ui_render_cv.notify_all(); + if (ui_renderer.joinable()) + ui_renderer.join(); if (hmd_renderer.joinable()) hmd_renderer.join(); App::I.terminate(); PostQuitMessage(0); + render_running = false; + if (render_thread.joinable()) + render_thread.join(); return 0; } else @@ -1170,12 +1270,14 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) auto h = (float)HIWORD(lp); if (h != 0 && running == 1) { - async_locker lock; - App::I.resize(w, h); - App::I.clear(); - App::I.redraw = true; - App::I.update(0.f); - SwapBuffers(hDC); + App::I.render_task([=] + { + App::I.resize(w, h); + App::I.clear(); + App::I.redraw = true; + App::I.update(0.f); + SwapBuffers(hDC); + }); } break; } @@ -1374,7 +1476,6 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) break; case WM_MOUSEWHEEL: { - async_locker lock; POINT pt; pt.x = GET_X_LPARAM(lp); pt.y = GET_Y_LPARAM(lp); diff --git a/src/node_colorwheel.cpp b/src/node_colorwheel.cpp index 52e35f0..b0b4045 100644 --- a/src/node_colorwheel.cpp +++ b/src/node_colorwheel.cpp @@ -2,6 +2,7 @@ #include "node_colorwheel.h" #include "shader.h" #include "log.h" +#include "app.h" Node* NodeColorWheel::clone_instantiate() const { @@ -42,21 +43,24 @@ void NodeColorWheel::loaded() vertices.push_back({{glm::cos(2.f/3.f*glm::pi())*l,glm::sin(2.f/3.f*glm::pi())*l,0,1},{0,0},{0,0,0,1}}); vertices.push_back({{l,0,0,1},{1,1},{1,0,0,1}}); - glGenBuffers(1, &buffers); - glBindBuffer(GL_ARRAY_BUFFER, buffers); - glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(vertex_t), vertices.data(), GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); + App::I.render_task([&] + { + glGenBuffers(1, &buffers); + glBindBuffer(GL_ARRAY_BUFFER, buffers); + glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(vertex_t), vertices.data(), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); - glGenVertexArrays(1, &arrays); - glBindVertexArray(arrays); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glEnableVertexAttribArray(2); - glBindBuffer(GL_ARRAY_BUFFER, buffers); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs)); - glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, col)); - glBindVertexArray(0); + glGenVertexArrays(1, &arrays); + glBindVertexArray(arrays); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glBindBuffer(GL_ARRAY_BUFFER, buffers); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)0); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs)); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, col)); + glBindVertexArray(0); + }); } void NodeColorWheel::draw() diff --git a/src/node_panel_grid.cpp b/src/node_panel_grid.cpp index 3db15b5..50edddf 100644 --- a/src/node_panel_grid.cpp +++ b/src/node_panel_grid.cpp @@ -151,11 +151,14 @@ void NodePanelGrid::init_controls() m_texture.create_mipmaps(); #else // get the texture data and resize it - m_texture.bind(); Image img; img.create(m_texture.size().x, m_texture.size().y); - glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.m_data.get()); - m_texture.unbind(); + App::I.render_task([&] + { + m_texture.bind(); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.m_data.get()); + m_texture.unbind(); + }); Image resized = img.resize(texres, texres); m_texture.create(resized); m_texture.create_mipmaps(); @@ -169,9 +172,6 @@ void NodePanelGrid::init_controls() m_plane.create<1>(1, 1); TextureManager::load("data/sun.png"); - - //glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, m_line_range); - //glGetFloatv(GL_ALIASED_LINE_WIDTH_GRANULARITY, &m_line_granularity); } int NodePanelGrid::get_samples() const diff --git a/src/node_stroke_preview.cpp b/src/node_stroke_preview.cpp index 8365a73..217fcd5 100644 --- a/src/node_stroke_preview.cpp +++ b/src/node_stroke_preview.cpp @@ -514,13 +514,11 @@ void NodeStrokePreview::draw_stroke() // Good luck, future Omar std::this_thread::sleep_for(std::chrono::seconds(1)); #endif - App::I.async_start(); m_sampler_linear.create(); m_sampler_linear_repeat.create(GL_LINEAR, GL_REPEAT); m_sampler_mipmap.create(); m_sampler_mipmap.set_filter(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR); m_brush_shape.create(); - App::I.async_end(); while (s_running) { auto node = s_queue.Get(); @@ -530,43 +528,41 @@ void NodeStrokePreview::draw_stroke() bool to_unload = (node->m_brush->m_tip_texture == nullptr); node->m_brush->preload(); - node->async_start(); - - gl_state gl; - gl.save(); - - auto new_size = node->m_preview_size; - if (!node->m_tex_preview.ready() || node->m_tex_preview.size() != new_size) - node->m_tex_preview.create((int)new_size.x, (int)new_size.y); - if (m_tex.size() != new_size) + App::I.render_task([node, to_unload] { - m_rtt.create((int)new_size.x, (int)new_size.y); - m_rtt_mixer.create((int)new_size.x, (int)new_size.y); - m_tex.create((int)new_size.x, (int)new_size.y); - m_tex_dual.create((int)new_size.x, (int)new_size.y); - m_tex_background.create((int)new_size.x, (int)new_size.y); - } + gl_state gl; + gl.save(); + + auto new_size = node->m_preview_size; + if (!node->m_tex_preview.ready() || node->m_tex_preview.size() != new_size) + node->m_tex_preview.create((int)new_size.x, (int)new_size.y); + if (m_tex.size() != new_size) + { + m_rtt.create((int)new_size.x, (int)new_size.y); + m_rtt_mixer.create((int)new_size.x, (int)new_size.y); + m_tex.create((int)new_size.x, (int)new_size.y); + m_tex_dual.create((int)new_size.x, (int)new_size.y); + m_tex_background.create((int)new_size.x, (int)new_size.y); + } + + node->m_brush->load(); + node->draw_stroke_immediate(); + if (to_unload) + node->m_brush->unload(); + + gl.restore(); + }); - node->m_brush->load(); - node->draw_stroke_immediate(); - if (to_unload) - node->m_brush->unload(); - - gl.restore(); - node->app_redraw(); - node->async_end(); //std::this_thread::sleep_for(std::chrono::milliseconds(30)); std::this_thread::yield(); } } - App::I.async_start(); m_rtt.destroy(); m_rtt_mixer.destroy(); m_tex.destroy(); m_tex_dual.destroy(); m_tex_background.destroy(); m_brush_shape.destroy(); - App::I.async_end(); }); } s_queue.mutex.unlock(); diff --git a/src/rtt.cpp b/src/rtt.cpp index e359f48..d3519f3 100644 --- a/src/rtt.cpp +++ b/src/rtt.cpp @@ -2,6 +2,7 @@ #include "log.h" #include "rtt.h" #include "util.h" +#include "app.h" RTT::RTT() { @@ -38,19 +39,22 @@ void RTT::resize(int width, int height) { RTT new_rtt; - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldDFboID); - glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldRFboID); + App::I.render_task([&] + { + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldDFboID); + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldRFboID); - new_rtt.create(width, height, -1, int_fmt, rboID != 0); + new_rtt.create(width, height, -1, int_fmt, rboID != 0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, new_rtt.fboID); - glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID); - glBlitFramebuffer(0, 0, w, h, 0, 0, new_rtt.w, new_rtt.h, GL_COLOR_BUFFER_BIT, GL_LINEAR); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, new_rtt.fboID); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID); + glBlitFramebuffer(0, 0, w, h, 0, 0, new_rtt.w, new_rtt.h, GL_COLOR_BUFFER_BIT, GL_LINEAR); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oldDFboID); - glBindFramebuffer(GL_READ_FRAMEBUFFER, oldRFboID); - - destroy(); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oldDFboID); + glBindFramebuffer(GL_READ_FRAMEBUFFER, oldRFboID); + + destroy(); + }); oldRFboID = 0; oldDFboID = 0; @@ -64,22 +68,25 @@ void RTT::resize(int width, int height) void RTT::destroy() { - if (rboID) + App::I.render_task_async([rboID=rboID, texID=texID, fboID=fboID] { - glDeleteRenderbuffers(1, &rboID); - } - if (texID) - { - unbindTexture(); - glDeleteTextures(1, &texID); - //LOG("TEX rtt destroy %d", texID) - } - if (fboID) - { - unbindFramebuffer(); - glDeleteFramebuffers(1, &fboID); - //LOG("RTT DESTROY %d", fboID); - } + if (rboID) + { + glDeleteRenderbuffers(1, &rboID); + } + if (texID) + { + //unbindTexture(); + glDeleteTextures(1, &texID); + //LOG("TEX rtt destroy %d", texID) + } + if (fboID) + { + //unbindFramebuffer(); + glDeleteFramebuffers(1, &fboID); + //LOG("RTT DESTROY %d", fboID); + } + }); texID = 0; fboID = 0; rboID = 0; @@ -89,101 +96,109 @@ void RTT::destroy() void RTT::copy(const RTT & source) { - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldDFboID); - glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldRFboID); + App::I.render_task([&] + { + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldDFboID); + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldRFboID); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboID); - glBindFramebuffer(GL_READ_FRAMEBUFFER, source.fboID); - glBlitFramebuffer(0, 0, source.w, source.h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_LINEAR); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboID); + glBindFramebuffer(GL_READ_FRAMEBUFFER, source.fboID); + glBlitFramebuffer(0, 0, source.w, source.h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_LINEAR); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oldDFboID); - glBindFramebuffer(GL_READ_FRAMEBUFFER, oldRFboID); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oldDFboID); + glBindFramebuffer(GL_READ_FRAMEBUFFER, oldRFboID); + }); } bool RTT::create(int width, int height, int tex/* = -1*/, GLint internal_format, bool depth_buffer /*= false*/) { - // Destroy any previously created object - destroy(); - - w = width; - h = height; - int_fmt = internal_format; - - if (tex == -1) + GLenum status = 0; + App::I.render_task([&] { - glGenTextures(1, &texID); - //LOG("TEX rtt create %d", texID); - } - else - { - texID = tex; - } + // Destroy any previously created object + destroy(); - auto ifmt = GL_UNSIGNED_BYTE; - if (internal_format == GL_RGBA32F) ifmt = GL_FLOAT; - if (internal_format == GL_RGBA16F) ifmt = GL_HALF_FLOAT; - - glBindTexture(GL_TEXTURE_2D, texID); - if (tex == -1) - glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, GL_RGBA, ifmt, 0); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glBindTexture(GL_TEXTURE_2D, 0); + w = width; + h = height; + int_fmt = internal_format; - // Create a renderbuffer object to store depth info - if (depth_buffer) - { - glGenRenderbuffers(1, &rboID); - glBindRenderbuffer(GL_RENDERBUFFER, rboID); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height); - glBindRenderbuffer(GL_RENDERBUFFER, 0); - } - - GLint oldFboID; - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldFboID); - - // Create a framebuffer object - glGenFramebuffers(1, &fboID); - glBindFramebuffer(GL_FRAMEBUFFER, fboID); - //LOG("RTT CREATE %d - tex %d", fboID, texID); - - // Attach the texture to FBO color attachment point - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texID, 0); - if (depth_buffer) - { - // Attach the renderbuffer to depth attachment point - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboID); - } - - auto err2str = [](GLenum err) { - switch (err) + if (tex == -1) { - case GL_FRAMEBUFFER_UNDEFINED: return "GL_FRAMEBUFFER_UNDEFINED"; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; - case GL_FRAMEBUFFER_UNSUPPORTED: return "GL_FRAMEBUFFER_UNSUPPORTED"; - case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; - default: return "UNKNOWN"; + glGenTextures(1, &texID); + //LOG("TEX rtt create %d", texID); + } + else + { + texID = tex; } - }; - // Check FBO status - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) - LOG("RTT::create failed because: %s", err2str(status)); + auto ifmt = GL_UNSIGNED_BYTE; + if (internal_format == GL_RGBA32F) ifmt = GL_FLOAT; + if (internal_format == GL_RGBA16F) ifmt = GL_HALF_FLOAT; - // Switch back to window-system-provided framebuffer - glBindFramebuffer(GL_FRAMEBUFFER, oldFboID); - oldRFboID = 0; - oldDFboID = 0; + glBindTexture(GL_TEXTURE_2D, texID); + if (tex == -1) + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, GL_RGBA, ifmt, 0); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D, 0); + + // Create a renderbuffer object to store depth info + if (depth_buffer) + { + glGenRenderbuffers(1, &rboID); + glBindRenderbuffer(GL_RENDERBUFFER, rboID); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + } + + GLint oldFboID; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldFboID); + + // Create a framebuffer object + glGenFramebuffers(1, &fboID); + glBindFramebuffer(GL_FRAMEBUFFER, fboID); + //LOG("RTT CREATE %d - tex %d", fboID, texID); + + // Attach the texture to FBO color attachment point + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texID, 0); + if (depth_buffer) + { + // Attach the renderbuffer to depth attachment point + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboID); + } + + auto err2str = [](GLenum err) { + switch (err) + { + case GL_FRAMEBUFFER_UNDEFINED: return "GL_FRAMEBUFFER_UNDEFINED"; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; + case GL_FRAMEBUFFER_UNSUPPORTED: return "GL_FRAMEBUFFER_UNSUPPORTED"; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; + default: return "UNKNOWN"; + } + }; + + // Check FBO status + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) + LOG("RTT::create failed because: %s", err2str(status)); + + // Switch back to window-system-provided framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, oldFboID); + oldRFboID = 0; + oldDFboID = 0; + }); return status == GL_FRAMEBUFFER_COMPLETE; } void RTT::bindFramebuffer() { + assert(App::I.is_render_thread()); #ifdef _DEBUG if (bound) { @@ -193,15 +208,16 @@ void RTT::bindFramebuffer() #endif } #endif // _DEBUG - bound = true; glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldDFboID); glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldRFboID); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboID); glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID); + bound = true; } void RTT::unbindFramebuffer() { + assert(App::I.is_render_thread()); if (!bound) return; glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oldDFboID); @@ -213,6 +229,7 @@ void RTT::unbindFramebuffer() void RTT::clear(glm::vec4 color) { + assert(App::I.is_render_thread()); glClearColor(color.r, color.g, color.b, color.a); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } @@ -240,11 +257,14 @@ uint8_t* RTT::readTextureData(uint8_t* buffer) { if (!buffer) buffer = createBuffer(); - GLint old; - glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old); - glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID); - glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, buffer); - glBindFramebuffer(GL_READ_FRAMEBUFFER, old); + App::I.render_task([&] + { + GLint old; + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID); + glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + glBindFramebuffer(GL_READ_FRAMEBUFFER, old); + }); return buffer; } @@ -252,11 +272,14 @@ float* RTT::readTextureDataFloat(float* buffer) { if (!buffer) buffer = createBufferFloat(); - GLint old; - glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old); - glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID); - glReadPixels(0, 0, w, h, GL_RGBA, GL_FLOAT, buffer); - glBindFramebuffer(GL_READ_FRAMEBUFFER, old); + App::I.render_task([&] + { + GLint old; + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID); + glReadPixels(0, 0, w, h, GL_RGBA, GL_FLOAT, buffer); + glBindFramebuffer(GL_READ_FRAMEBUFFER, old); + }); return buffer; } @@ -272,11 +295,13 @@ float * RTT::createBufferFloat() void RTT::bindTexture() { + assert(App::I.is_render_thread()); glBindTexture(GL_TEXTURE_2D, texID); } void RTT::unbindTexture() { + assert(App::I.is_render_thread()); glBindTexture(GL_TEXTURE_2D, 0); } diff --git a/src/shader.cpp b/src/shader.cpp index fec9bf5..d4e4252 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -2,6 +2,7 @@ #include "log.h" #include "shader.h" #include "asset.h" +#include "app.h" std::map ShaderManager::m_shaders; Shader* ShaderManager::m_current; @@ -153,122 +154,135 @@ bool Shader::reload() bool Shader::create(const char* vertex, const char* fragment) { - GLint status; - static char infolog[4096]; - int infolen; - const GLchar* source; - - auto vs = glCreateShader(GL_VERTEX_SHADER); - if (!vs) + bool ret = true; + App::I.render_task([&] { - return false; - } - source = vertex; - glShaderSource(vs, 1, &source, nullptr); - glCompileShader(vs); - glGetShaderiv(vs, GL_COMPILE_STATUS, &status); - glGetShaderInfoLog(vs, sizeof(infolog), &infolen, infolog); - if (infolen > 0) - { - LOG("\nVERTEX SHADER: %s", m_path.c_str()); - parse_error(infolog, vertex); - } - if (status == 0) - { - glDeleteShader(vs); - return false; - } - - auto fs = glCreateShader(GL_FRAGMENT_SHADER); - if (!fs) - { - glDeleteShader(vs); - return false; - } - source = fragment; - glShaderSource(fs, 1, &source, nullptr); - glCompileShader(fs); - glGetShaderiv(fs, GL_COMPILE_STATUS, &status); - glGetShaderInfoLog(fs, sizeof(infolog), &infolen, infolog); - if (infolen > 0) - { - LOG("\nFRAGMENT SHADER: %s", m_path.c_str()); - parse_error(infolog, fragment); - } - if (status == 0) - { - glDeleteShader(vs); - glDeleteShader(fs); - return false; - } - - auto ps = glCreateProgram(); - if (!ps) - { - glDeleteShader(vs); - glDeleteShader(fs); - return false; - } - glAttachShader(ps, vs); - glAttachShader(ps, fs); - glDeleteShader(vs); - glDeleteShader(fs); - - glLinkProgram(ps); - if (glGetAttribLocation(ps, "pos") != -1) - glBindAttribLocation(ps, 0, "pos"); - - if (glGetAttribLocation(ps, "uvs") != -1) - glBindAttribLocation(ps, 1, "uvs"); - - if (glGetAttribLocation(ps, "uvs2") != -1) - glBindAttribLocation(ps, 2, "uvs2"); + GLint status; + static char infolog[4096]; + int infolen; + const GLchar* source; - if (glGetAttribLocation(ps, "col") != -1) - glBindAttribLocation(ps, 3, "col"); - - if (glGetAttribLocation(ps, "nor") != -1) - glBindAttribLocation(ps, 3, "nor"); - - glLinkProgram(ps); - glGetProgramiv(ps, GL_LINK_STATUS, &status); - glGetProgramInfoLog(ps, sizeof(infolog), &infolen, infolog); - if (infolen > 0) - LOG("LINK SHADER: %s\n%s", m_path.c_str(), infolog); - if (status == 0) - { - glDeleteProgram(ps); - return false; - } - - // Parse shader uniforms - { - GLint count; - GLint size; // size of the variable - GLenum type; // type of the variable (float, vec3 or mat4, etc) - const GLsizei bufSize = 16; // maximum name length - GLchar name[bufSize]; // variable name in GLSL - GLsizei length; // name length - glGetProgramiv(ps, GL_ACTIVE_UNIFORMS, &count); - for (int i = 0; i < count; i++) + auto vs = glCreateShader(GL_VERTEX_SHADER); + if (!vs) { - glGetActiveUniform(ps, (GLuint)i, bufSize, &length, &size, &type, name); - m_umap[(kShaderUniform)const_hash(name)] = glGetUniformLocation(ps, name); - //printf("Uniform #%d Type: %u Name: %s Loc: %d\n", i, type, name, glGetUniformLocation(ps, name)); + ret = false; + return; } - } + source = vertex; + glShaderSource(vs, 1, &source, nullptr); + glCompileShader(vs); + glGetShaderiv(vs, GL_COMPILE_STATUS, &status); + glGetShaderInfoLog(vs, sizeof(infolog), &infolen, infolog); + if (infolen > 0) + { + LOG("\nVERTEX SHADER: %s", m_path.c_str()); + parse_error(infolog, vertex); + } + if (status == 0) + { + glDeleteShader(vs); + ret = false; + return; + } + + auto fs = glCreateShader(GL_FRAGMENT_SHADER); + if (!fs) + { + glDeleteShader(vs); + ret = false; + return; + } + source = fragment; + glShaderSource(fs, 1, &source, nullptr); + glCompileShader(fs); + glGetShaderiv(fs, GL_COMPILE_STATUS, &status); + glGetShaderInfoLog(fs, sizeof(infolog), &infolen, infolog); + if (infolen > 0) + { + LOG("\nFRAGMENT SHADER: %s", m_path.c_str()); + parse_error(infolog, fragment); + } + if (status == 0) + { + glDeleteShader(vs); + glDeleteShader(fs); + ret = false; + return; + } + + auto ps = glCreateProgram(); + if (!ps) + { + glDeleteShader(vs); + glDeleteShader(fs); + ret = false; + return; + } + glAttachShader(ps, vs); + glAttachShader(ps, fs); + glDeleteShader(vs); + glDeleteShader(fs); + + glLinkProgram(ps); + if (glGetAttribLocation(ps, "pos") != -1) + glBindAttribLocation(ps, 0, "pos"); + + if (glGetAttribLocation(ps, "uvs") != -1) + glBindAttribLocation(ps, 1, "uvs"); + + if (glGetAttribLocation(ps, "uvs2") != -1) + glBindAttribLocation(ps, 2, "uvs2"); + + if (glGetAttribLocation(ps, "col") != -1) + glBindAttribLocation(ps, 3, "col"); + + if (glGetAttribLocation(ps, "nor") != -1) + glBindAttribLocation(ps, 3, "nor"); + + glLinkProgram(ps); + glGetProgramiv(ps, GL_LINK_STATUS, &status); + glGetProgramInfoLog(ps, sizeof(infolog), &infolen, infolog); + if (infolen > 0) + LOG("LINK SHADER: %s\n%s", m_path.c_str(), infolog); + if (status == 0) + { + glDeleteProgram(ps); + ret = false; + return; + } + + // Parse shader uniforms + { + GLint count; + GLint size; // size of the variable + GLenum type; // type of the variable (float, vec3 or mat4, etc) + const GLsizei bufSize = 16; // maximum name length + GLchar name[bufSize]; // variable name in GLSL + GLsizei length; // name length + glGetProgramiv(ps, GL_ACTIVE_UNIFORMS, &count); + for (int i = 0; i < count; i++) + { + glGetActiveUniform(ps, (GLuint)i, bufSize, &length, &size, &type, name); + m_umap[(kShaderUniform)const_hash(name)] = glGetUniformLocation(ps, name); + //printf("Uniform #%d Type: %u Name: %s Loc: %d\n", i, type, name, glGetUniformLocation(ps, name)); + } + } + + prog = ps; + }); - prog = ps; - - return true; + return ret; } void Shader::destroy() { if (prog) { - glUseProgram(0); - glDeleteProgram(prog); + App::I.render_task_async([prog=prog] + { + glUseProgram(0); + glDeleteProgram(prog); + }); prog = 0; } m_umap.clear(); @@ -276,16 +290,19 @@ void Shader::destroy() void Shader::use() { + assert(App::I.is_render_thread()); glUseProgram(prog); } void Shader::u_vec4(kShaderUniform id, const glm::vec4& v) { - if (m_umap.count(id) == 0) + assert(App::I.is_render_thread()); + if (m_umap.count(id) == 0) LOG("UNIFORM vec4 %d NOT FOUND in shader %d", (int)id, (int)name) else glUniform4fv(m_umap[id], 1, glm::value_ptr(v)); } void Shader::u_vec3(kShaderUniform id, const glm::vec3& v) { + assert(App::I.is_render_thread()); if (m_umap.count(id) == 0) LOG("UNIFORM vec3 %d NOT FOUND in shader %d", (int)id, (int)name) else glUniform3fv(m_umap[id], 1, glm::value_ptr(v)); @@ -293,19 +310,22 @@ void Shader::u_vec3(kShaderUniform id, const glm::vec3& v) void Shader::u_vec2(kShaderUniform id, const glm::vec2& v) { - if (m_umap.count(id) == 0) + assert(App::I.is_render_thread()); + if (m_umap.count(id) == 0) LOG("UNIFORM vec2 %d NOT FOUND in shader %d", (int)id, (int)name) else glUniform2fv(m_umap[id], 1, glm::value_ptr(v)); } void Shader::u_mat4(kShaderUniform id, const glm::mat4& m) { - if (m_umap.count(id) == 0) + assert(App::I.is_render_thread()); + if (m_umap.count(id) == 0) LOG("UNIFORM mat4 %d NOT FOUND in shader %d", (int)id, (int)name) else glUniformMatrix4fv(m_umap[id], 1, GL_FALSE, glm::value_ptr(m)); } void Shader::u_int(kShaderUniform id, int i) { + assert(App::I.is_render_thread()); if (m_umap.count(id) == 0) LOG("UNIFORM int %d NOT FOUND in shader %d", (int)id, (int)name) else @@ -313,12 +333,14 @@ void Shader::u_int(kShaderUniform id, int i) } void Shader::u_float(kShaderUniform id, float f) { - if (m_umap.count(id) == 0) + assert(App::I.is_render_thread()); + if (m_umap.count(id) == 0) LOG("UNIFORM float %d NOT FOUND in shader %d", (int)id, (int)name) else glUniform1f(m_umap[id], f); } GLint Shader::GetAttribLocation(const char* name) { + assert(App::I.is_render_thread()); return glGetAttribLocation(prog, name); } diff --git a/src/shape.cpp b/src/shape.cpp index a26c37c..a7eeec0 100644 --- a/src/shape.cpp +++ b/src/shape.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "log.h" #include "shape.h" +#include "app.h" bool Shape::create_buffers(GLushort * idx, GLvoid * vertices, int isize, int vsize) { @@ -20,79 +21,99 @@ bool Shape::create_buffers_imp(GLvoid* idx, GLvoid* vertices, int isize, int vsi { use_idx = true; - destroy(); - - glGenBuffers(2, buffers); - if (!(buffers[0] && buffers[1])) - return false; - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, isize, idx, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); - glBufferData(GL_ARRAY_BUFFER, vsize, vertices, GL_STATIC_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - -#if USE_VBO - glGenVertexArrays(2, arrays); - if (!(arrays[0] && arrays[1])) - return false; - for (int i = 0; i < 2; i++) + bool ret = false; + App::I.render_task([&] { - glBindVertexArray(arrays[i]); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glEnableVertexAttribArray(2); - glEnableVertexAttribArray(3); + destroy(); + + glGenBuffers(2, buffers); + if (!(buffers[0] && buffers[1])) + { + ret = false; + return; + } + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, isize, idx, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs)); - glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs2)); - glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, nor)); - } - glBindVertexArray(0); + glBufferData(GL_ARRAY_BUFFER, vsize, vertices, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + +#if USE_VBO + glGenVertexArrays(2, arrays); + if (!(arrays[0] && arrays[1])) + { + ret = false; + return; + } + for (int i = 0; i < 2; i++) + { + glBindVertexArray(arrays[i]); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)0); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs)); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs2)); + glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, nor)); + } + glBindVertexArray(0); #endif - return true; + }); + return ret; } bool Shape::create_buffers(GLvoid* vertices, int vsize) { use_idx = false; - destroy(); + bool ret = false; + App::I.render_task([&] + { + destroy(); + + glGenBuffers(1, buffers); + if (!buffers[0]) + { + ret = false; + return; + } + + if (vsize) + { + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glBufferData(GL_ARRAY_BUFFER, vsize, vertices, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } - glGenBuffers(1, buffers); - if (!buffers[0]) - return false; - - if (vsize) - { - glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); - glBufferData(GL_ARRAY_BUFFER, vsize, vertices, GL_STATIC_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - #if USE_VBO - glGenVertexArrays(2, arrays); - if (!(arrays[0] && arrays[1])) - return false; - for (int i = 0; i < 2; i++) - { - glBindVertexArray(arrays[i]); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glEnableVertexAttribArray(2); - glEnableVertexAttribArray(3); - glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs)); - glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs2)); - glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, nor)); - } - glBindVertexArray(0); + glGenVertexArrays(2, arrays); + if (!(arrays[0] && arrays[1])) + { + ret = false; + return; + } + for (int i = 0; i < 2; i++) + { + glBindVertexArray(arrays[i]); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)0); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs)); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs2)); + glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, nor)); + } + glBindVertexArray(0); #endif - return true; + }); + return ret; } void Shape::draw_fill() const { @@ -103,31 +124,34 @@ void Shape::draw_fill() const type = GL_POINTS; if (count[0] == 2) type = GL_LINES; -#if USE_VBO - glBindVertexArray(arrays[0]); - if (use_idx) - glDrawElements(type, count[0], index_type, ioff[0]); - else - glDrawArrays(type, 0, count[0]); - glBindVertexArray(0); -#else - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs)); - if (use_idx) + App::I.render_task([=] { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); - glDrawElements(type, count[0], GL_UNSIGNED_SHORT, ioff[0]); - } - else - glDrawArrays(type, 0, count[0]); - glDisableVertexAttribArray(0); - glDisableVertexAttribArray(1); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +#if USE_VBO + glBindVertexArray(arrays[0]); + if (use_idx) + glDrawElements(type, count[0], index_type, ioff[0]); + else + glDrawArrays(type, 0, count[0]); + glBindVertexArray(0); +#else + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)0); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs)); + if (use_idx) + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); + glDrawElements(type, count[0], GL_UNSIGNED_SHORT, ioff[0]); + } + else + glDrawArrays(type, 0, count[0]); + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); #endif // USE_VBO + }); } void Shape::draw_stroke() const { @@ -136,32 +160,61 @@ void Shape::draw_stroke() const GLenum type = GL_LINES; if (count[1] == 1) type = GL_POINTS; -#if USE_VBO - glBindVertexArray(arrays[1]); - if (use_idx) - glDrawElements(type, count[1], index_type, ioff[1]); - else - glDrawArrays(type, 0, count[1]); - glBindVertexArray(0); -#else - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); - glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs)); - if (use_idx) + App::I.render_task([=] { +#if USE_VBO + glBindVertexArray(arrays[1]); + if (use_idx) + glDrawElements(type, count[1], index_type, ioff[1]); + else + glDrawArrays(type, 0, count[1]); + glBindVertexArray(0); +#else + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); - glDrawElements(type, count[1], GL_UNSIGNED_SHORT, ioff[1]); - } - else - glDrawArrays(type, 0, count[1]); - glDisableVertexAttribArray(0); - glDisableVertexAttribArray(1); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)0); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs)); + if (use_idx) + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); + glDrawElements(type, count[1], GL_UNSIGNED_SHORT, ioff[1]); + } + else + glDrawArrays(type, 0, count[1]); + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); #endif // USE_VBO + }); +} + +void Shape::destroy() +{ + App::I.render_task_async([b1=buffers[0],b2=buffers[1],a1=arrays[0],a2=arrays[1]] + { + if (b1 || b2) + { + glDeleteBuffers(1, &b1); + glDeleteBuffers(1, &b2); + } +#if USE_VBO + if (a1 || a2) + { + glDeleteVertexArrays(1, &a1); + glDeleteVertexArrays(1, &a2); + } +#endif // USE_VBO + }); + buffers[0] = buffers[1] = 0; + arrays[0] = arrays[1] = 0; +} + +Shape::~Shape() +{ + destroy(); } bool RectShape::create(float w, float h) @@ -437,21 +490,24 @@ void Plane::update_vertices(const glm::vec4* data, const glm::vec2* uvs, const g vertices[i].pos.z = q; } - glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - static GLushort idx[6 + 8]{ - 0, 1, 2, - 0, 2, 3, - 0, 1, - 1, 2, - 2, 3, - 3, 0, - }; - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(idx), idx, GL_STATIC_DRAW); + App::I.render_task([this] + { + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + static GLushort idx[6 + 8]{ + 0, 1, 2, + 0, 2, 3, + 0, 1, + 1, 2, + 2, 3, + 3, 0, + }; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(idx), idx, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + }); } void Circle::create_impl(float radius, int div, GLushort* idx, vertex_t* vertices) { @@ -704,18 +760,24 @@ void Sphere::create_impl(int rings, int sectors, float radius, } void LineSegment::update_vertices(const glm::vec4 data[2]) { - static vertex_t vertices[2]; - vertices[0] = { data[0], { 0, 0 } }; // A - vertices[1] = { data[1], { 0, 1 } }; // B - glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); + App::I.render_task([&] + { + static vertex_t vertices[2]; + vertices[0] = { data[0], { 0, 0 } }; // A + vertices[1] = { data[1], { 0, 1 } }; // B + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + }); } void DynamicShape::update_vertices(vertex_t* vertices, int vcount) { - count[0] = vcount; - count[1] = vcount; - glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_t) * vcount, vertices, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); + App::I.render_task([&] + { + count[0] = vcount; + count[1] = vcount; + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_t) * vcount, vertices, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + }); } diff --git a/src/shape.h b/src/shape.h index 8ff8c0f..6b5af74 100644 --- a/src/shape.h +++ b/src/shape.h @@ -25,22 +25,9 @@ public: bool create_buffers(GLvoid* vertices, int vsize); void draw_fill() const; void draw_stroke() const; - void destroy() - { - if (buffers[0] || buffers[1]) - glDeleteBuffers(2, buffers); -#if USE_VBO - if (arrays[0] || arrays[1]) - glDeleteVertexArrays(2, arrays); -#endif // USE_VBO - buffers[0] = buffers[1] = 0; - arrays[0] = arrays[1] = 0; - } + void destroy(); virtual bool create_attrib() { return true; }; - ~Shape() - { - destroy(); - } + ~Shape(); protected: glm::vec2 quad_mid_point(glm::vec2 a, glm::vec2 b, glm::vec2 c, glm::vec2 d) { diff --git a/src/texture.cpp b/src/texture.cpp index 503f0eb..368503d 100644 --- a/src/texture.cpp +++ b/src/texture.cpp @@ -41,19 +41,22 @@ void TextureManager::invalidate() bool Texture2D::create(int width, int height, GLint internal_format, GLint format, const uint8_t* data) { - destroy(); - m_width = width; - m_height = height; - m_format = format; - m_iformat = internal_format; - glGenTextures(1, &m_tex); - //LOG("TEX create %d", m_tex); - bind(); - auto ifmt = GL_UNSIGNED_BYTE; - if (internal_format == GL_RGBA32F) ifmt = GL_FLOAT; - if (internal_format == GL_RGBA16F) ifmt = GL_HALF_FLOAT; - glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, ifmt, data); - unbind(); + App::I.render_task([=] + { + destroy(); + m_width = width; + m_height = height; + m_format = format; + m_iformat = internal_format; + glGenTextures(1, &m_tex); + //LOG("TEX create %d", m_tex); + bind(); + auto ifmt = GL_UNSIGNED_BYTE; + if (internal_format == GL_RGBA32F) ifmt = GL_FLOAT; + if (internal_format == GL_RGBA16F) ifmt = GL_HALF_FLOAT; + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, ifmt, data); + unbind(); + }); return true; } bool Texture2D::create(const Image& img) @@ -65,10 +68,13 @@ bool Texture2D::create(const Image& img) void Texture2D::create_mipmaps() { - bind(); - glGenerateMipmap(GL_TEXTURE_2D); - unbind(); - has_mips = true; + App::I.render_task([this] + { + bind(); + glGenerateMipmap(GL_TEXTURE_2D); + unbind(); + has_mips = true; + }); } void Texture2D::assign(GLuint tex, int w/* = -1*/, int h/* = -1*/, GLuint internal_format/* = GL_RGBA8*/, GLuint format/* = GL_RGBA*/) @@ -98,10 +104,37 @@ bool Texture2D::load_file(std::string filename) return create(img); } +void Texture2D::destroy() +{ + if (m_tex) + { + App::I.render_task_async([id = m_tex] + { + glDeleteTextures(1, &id); + }); + m_tex = 0; + } +} + +void Texture2D::bind() const +{ + assert(App::I.is_render_thread()); + glBindTexture(GL_TEXTURE_2D, m_tex); +} + +void Texture2D::unbind() const +{ + assert(App::I.is_render_thread()); + glBindTexture(GL_TEXTURE_2D, 0); +} + void Texture2D::update(const uint8_t* data) { - bind(); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_width, m_height, m_format, GL_UNSIGNED_BYTE, data); + App::I.render_task([this, data] + { + bind(); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_width, m_height, m_format, GL_UNSIGNED_BYTE, data); + }); } glm::vec2 Texture2D::size() const @@ -113,57 +146,81 @@ Texture2D::~Texture2D() { if (auto_destroy) { - LOG("Texture2D auto destroy"); - App::I.async_start(); - destroy(); - App::I.async_end(); + App::I.render_task_async([this] + { + LOG("Texture2D auto destroy"); + destroy(); + }); } } bool Sampler::create(GLint filter /*= GL_LINEAR*/, GLint wrap /*= GL_CLAMP_TO_EDGE*/) { + bool ret = false; + App::I.render_task([this, &ret, filter, wrap] + { #if USE_SAMPLER - glGenSamplers(1, &id); + glGenSamplers(1, &id); #endif // USE_SAMPLER - if (id == 0) - return false; - set(filter, wrap); - return true; + if (id == 0) + { + ret = false; + return; + } + set(filter, wrap); + ret = true; + }); + return ret; } void Sampler::set(GLint filter /*= GL_LINEAR*/, GLint wrap /*= GL_CLAMP_TO_EDGE*/) { + App::I.render_task([=] + { #if USE_SAMPLER - glSamplerParameteri(id, GL_TEXTURE_WRAP_S, wrap); - glSamplerParameteri(id, GL_TEXTURE_WRAP_T, wrap); - glSamplerParameteri(id, GL_TEXTURE_WRAP_R, wrap); - glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, filter); - glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, filter); + glSamplerParameteri(id, GL_TEXTURE_WRAP_S, wrap); + glSamplerParameteri(id, GL_TEXTURE_WRAP_T, wrap); + glSamplerParameteri(id, GL_TEXTURE_WRAP_R, wrap); + glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, filter); + glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, filter); #endif // USE_SAMPLER + }); } void Sampler::set_filter(GLint filter_min, GLint filter_mag) { + App::I.render_task([=] + { #if USE_SAMPLER - glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, filter_min); - glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, filter_mag); + glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, filter_min); + glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, filter_mag); #endif // USE_SAMPLER + }); } void Sampler::set_border(glm::vec4 rgba) { + App::I.render_task([this, rgba] + { #if USE_SAMPLER && !defined(__IOS__) && !defined(__ANDROID__) - glSamplerParameterfv(id, GL_TEXTURE_BORDER_COLOR, glm::value_ptr(rgba)); + glSamplerParameterfv(id, GL_TEXTURE_BORDER_COLOR, glm::value_ptr(rgba)); #endif // USE_SAMPLER + }); } void Sampler::bind(int unit) const { - current_unit = unit; + App::I.render_task([=] + { + current_unit = unit; #if USE_SAMPLER - glBindSampler(unit, id); + glBindSampler(unit, id); #endif // USE_SAMPLER + }); } void Sampler::unbind() { + App::I.render_task([=] + { #if USE_SAMPLER - glBindSampler(current_unit, 0); + glBindSampler(current_unit, 0); #endif // USE_SAMPLER + }); } diff --git a/src/texture.h b/src/texture.h index 8dee49e..5414f19 100644 --- a/src/texture.h +++ b/src/texture.h @@ -16,9 +16,9 @@ public: void assign(GLuint tex, int w = -1, int h = -1, GLuint internal_format = GL_RGBA8, GLuint format = GL_RGBA); bool load(std::string filename); bool load_file(std::string filename); - void destroy() { if (m_tex) /*LOG("TEX destroy %d", m_tex);*/ glDeleteTextures(1, &m_tex); m_tex = 0; } - void bind() const { glBindTexture(GL_TEXTURE_2D, m_tex); } - void unbind() const { glBindTexture(GL_TEXTURE_2D, 0); } + void destroy(); + void bind() const; + void unbind() const; void update(const uint8_t* data); bool ready() const { return m_tex != 0; } void create_mipmaps();