render implement thread, wrap GL commands into tasks
This commit is contained in:
57
src/app.cpp
57
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<NodeMessageBox>();
|
||||
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,15 +380,9 @@ void App::upload(std::string filename, std::string name, std::function<void(floa
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
void handle_gl_callback(GLenum source, GLenum type, GLuint id,
|
||||
GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
|
||||
{
|
||||
static std::map<GLenum, int> colors = {
|
||||
@@ -407,17 +397,35 @@ void App::init()
|
||||
LOG("OPENGL: %.*s", length, message);
|
||||
FlushConsoleInputBuffer(GetStdHandle(STD_OUTPUT_HANDLE));
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), info.wAttributes);
|
||||
#ifdef _WIN32 && _DEBUG
|
||||
#ifdef _DEBUG
|
||||
if (severity == GL_DEBUG_SEVERITY_HIGH)
|
||||
__debugbreak();
|
||||
#endif
|
||||
}
|
||||
}, nullptr);
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
}
|
||||
#endif
|
||||
|
||||
void App::init()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (glDebugMessageCallback)
|
||||
{
|
||||
// colors: http://stackoverflow.com/questions/4053837/colorizing-text-in-the-console-with-c
|
||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info);
|
||||
|
||||
render_task([]
|
||||
{
|
||||
glDebugMessageCallback(handle_gl_callback, nullptr);
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG("Screen Resolution: %dx%d", (int)width, (int)height);
|
||||
|
||||
render_task([]
|
||||
{
|
||||
LOG("GL version: %s", glGetString(GL_VERSION));
|
||||
LOG("GL vendor: %s", glGetString(GL_VENDOR));
|
||||
LOG("GL renderer: %s", glGetString(GL_RENDERER));
|
||||
@@ -433,11 +441,6 @@ void App::init()
|
||||
// }
|
||||
//}
|
||||
|
||||
LOG("Screen Resolution: %dx%d", (int)width, (int)height);
|
||||
|
||||
//zoom = ceilf(width / 2000.f);
|
||||
//zoom = 2;
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
#if defined(_WIN32) || defined(__OSX__)
|
||||
glEnable(GL_PROGRAM_POINT_SIZE);
|
||||
@@ -445,6 +448,7 @@ void App::init()
|
||||
#endif
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glBlendEquationSeparate(GL_FUNC_ADD, GL_MAX);
|
||||
});
|
||||
|
||||
int run_counter = Settings::value<Serializer::Integer>("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)
|
||||
|
||||
65
src/app.h
65
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<typename T, typename R = std::result_of<T()>::type>
|
||||
std::future<R> render_task_async(T task)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
extern std::deque<std::packaged_task<R()>> render_tasklist;
|
||||
extern std::mutex render_task_mutex;
|
||||
extern std::condition_variable render_cv;
|
||||
std::packaged_task<R()> pt(task);
|
||||
std::future<R> f = pt.get_future();
|
||||
if (is_render_thread())
|
||||
{
|
||||
pt();
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(render_task_mutex);
|
||||
render_tasklist.push_back(std::move(pt));
|
||||
}
|
||||
render_cv.notify_all();
|
||||
}
|
||||
return f;
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
template<typename T, typename R = std::result_of<T()>::type>
|
||||
R render_task(T task)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
extern std::deque<std::packaged_task<R()>> render_tasklist;
|
||||
extern std::mutex render_task_mutex;
|
||||
extern std::condition_variable render_cv;
|
||||
std::packaged_task<R()> pt(task);
|
||||
std::future<R> f = pt.get_future();
|
||||
if (is_render_thread())
|
||||
{
|
||||
pt();
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> 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([] {});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -28,7 +28,6 @@ std::shared_ptr<NodeProgressBar> 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<NodeMessageBox>();
|
||||
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<NodeDialogNewDoc>();
|
||||
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<NodeDialogOpen>();
|
||||
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<NodeDialogBrowse>();
|
||||
dialog->m_manager = &layout;
|
||||
@@ -277,7 +269,6 @@ void App::dialog_browse()
|
||||
dialog->destroy();
|
||||
}
|
||||
};
|
||||
async_end();
|
||||
};
|
||||
|
||||
if (canvas)
|
||||
|
||||
@@ -9,6 +9,7 @@ void App::initShaders()
|
||||
std::logic_error("check_uniform_uniqueness() failed");
|
||||
#endif // _DEBUG
|
||||
|
||||
render_task([] {
|
||||
GLint n_exts;
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, &n_exts);
|
||||
for (int i = 0; i < n_exts; i++)
|
||||
@@ -19,6 +20,7 @@ void App::initShaders()
|
||||
ShaderManager::ext_framebuffer_fetch = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
LOG("Shader Extension shader_framebuffer_fetch: %s", ShaderManager::ext_framebuffer_fetch ? "enabled" : "disabled");
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
void BrushMesh::draw(const std::vector<StrokeSample>& samples, const glm::mat4& proj)
|
||||
{
|
||||
assert(App::I.is_render_thread());
|
||||
std::vector<instance_t> attributes;
|
||||
attributes.reserve(samples.size());
|
||||
for (const auto& s : samples)
|
||||
@@ -68,6 +69,9 @@ void BrushMesh::draw(const std::vector<StrokeSample>& samples, const glm::mat4&
|
||||
#endif // USE_VBO
|
||||
}
|
||||
bool BrushMesh::create()
|
||||
{
|
||||
bool ret = true;
|
||||
App::I.render_task([&]
|
||||
{
|
||||
static GLushort idx[6]{ 0, 1, 2, 0, 2, 3 };
|
||||
static vertex_t vertices[4]{
|
||||
@@ -78,7 +82,10 @@ bool BrushMesh::create()
|
||||
};
|
||||
glGenBuffers(3, buffers);
|
||||
if (!(buffers[0] && buffers[1] && buffers[2]))
|
||||
return false;
|
||||
{
|
||||
ret = false;
|
||||
return;
|
||||
}
|
||||
|
||||
static instance_t inst{ glm::mat4(1), .1f };
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffers[2]);
|
||||
@@ -99,7 +106,10 @@ bool BrushMesh::create()
|
||||
#if USE_VBO
|
||||
glGenVertexArrays(1, &vao);
|
||||
if (!vao)
|
||||
return false;
|
||||
{
|
||||
ret = false;
|
||||
return;
|
||||
}
|
||||
glBindVertexArray(vao);
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
@@ -125,8 +135,9 @@ bool BrushMesh::create()
|
||||
glVertexAttribDivisor(loc_flow, 1);
|
||||
glBindVertexArray(0);
|
||||
#endif
|
||||
});
|
||||
|
||||
return true;
|
||||
return ret;
|
||||
}
|
||||
StrokeSample Stroke::randomize_sample(const glm::vec3& pos, float pressure, float dir_angle)
|
||||
{
|
||||
|
||||
@@ -1474,10 +1474,13 @@ void Canvas::FloodData::apply()
|
||||
if (!dirty[plane])
|
||||
continue;
|
||||
auto& rtt = layer->m_rtt[plane];
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -2765,6 +2768,11 @@ bool Canvas::project_open_thread(std::string file_path)
|
||||
}
|
||||
|
||||
Image Canvas::thumbnail_generate(int w, int h)
|
||||
{
|
||||
Image image;
|
||||
image.create(w, h);
|
||||
|
||||
App::I.render_task([this, w, h, &image]
|
||||
{
|
||||
// save viewport and clear color states
|
||||
GLint vp[4];
|
||||
@@ -2859,8 +2867,6 @@ Image Canvas::thumbnail_generate(int w, int h)
|
||||
fb.unbindFramebuffer();
|
||||
|
||||
// read the rendered image
|
||||
Image image;
|
||||
image.create(w, h);
|
||||
fb.readTextureData((uint8_t*)image.data());
|
||||
|
||||
fb.destroy();
|
||||
@@ -2871,6 +2877,7 @@ Image Canvas::thumbnail_generate(int w, int h)
|
||||
glViewport(vp[0], vp[1], vp[2], vp[3]);
|
||||
glClearColor(cc[0], cc[1], cc[2], cc[3]);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
});
|
||||
|
||||
return image;
|
||||
}
|
||||
@@ -2903,6 +2910,8 @@ Image Canvas::thumbnail_read(std::string file_path)
|
||||
}
|
||||
|
||||
void Canvas::draw_objects_direct(std::function<void(const glm::mat4& camera, const glm::mat4& proj, int i)> observer, Layer& layer)
|
||||
{
|
||||
App::I.render_task([&]
|
||||
{
|
||||
// save viewport and clear color states
|
||||
GLint vp[4];
|
||||
@@ -2946,9 +2955,12 @@ void Canvas::draw_objects_direct(std::function<void(const glm::mat4& camera, con
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
draw_merge();
|
||||
});
|
||||
}
|
||||
|
||||
void Canvas::draw_objects(std::function<void(const glm::mat4& camera, const glm::mat4& proj, int i)> observer, Layer& layer)
|
||||
{
|
||||
App::I.render_task([&]
|
||||
{
|
||||
// save viewport and clear color states
|
||||
GLint vp[4];
|
||||
@@ -3038,6 +3050,7 @@ void Canvas::draw_objects(std::function<void(const glm::mat4& camera, const glm:
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
draw_merge();
|
||||
});
|
||||
}
|
||||
|
||||
void Canvas::draw_objects(std::function<void(const glm::mat4& camera, const glm::mat4& proj, int i)> 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,6 +3221,8 @@ void Layer::restore(const Snapshot& snap)
|
||||
// it's just a quick fix DON'T SHIP!!
|
||||
//m_rtt[i].recreate();
|
||||
|
||||
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,
|
||||
@@ -3218,15 +3232,14 @@ void Layer::restore(const Snapshot& snap)
|
||||
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<glm::vec4, 6> * dirty_box /*= nullptr*/, std::array<bool, 6> * 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,17 +3251,22 @@ Layer::Snapshot Layer::snapshot(std::array<glm::vec4, 6> * dirty_box /*= nullptr
|
||||
snap.image[i] = std::make_unique<uint8_t[]>(m_rtt[i].bytes());
|
||||
|
||||
//glReadBuffer(GL_BACK);
|
||||
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)
|
||||
{
|
||||
App::I.render_task([&]
|
||||
{
|
||||
// push clear color state
|
||||
GLfloat cc[4];
|
||||
@@ -3277,6 +3295,7 @@ void Layer::clear(const glm::vec4& c)
|
||||
|
||||
// restore clear color state
|
||||
glClearColor(cc[0], cc[1], cc[2], cc[3]);
|
||||
});
|
||||
}
|
||||
|
||||
bool Layer::create(int width, int height, std::string name)
|
||||
@@ -3284,6 +3303,8 @@ bool Layer::create(int width, int height, std::string name)
|
||||
m_name = name;
|
||||
w = width;
|
||||
h = height;
|
||||
App::I.render_task([&]
|
||||
{
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
m_rtt[i].create(width, height);
|
||||
@@ -3293,6 +3314,7 @@ bool Layer::create(int width, int height, std::string name)
|
||||
m_dirty_box[i] = glm::vec4(w, h, 0, 0); // reset bounding box
|
||||
m_dirty_face[i] = false;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include "font.h"
|
||||
#include "shader.h"
|
||||
#include "asset.h"
|
||||
#include "util.h"
|
||||
#include "app.h"
|
||||
|
||||
std::map<kFont, Font> FontManager::m_fonts;
|
||||
Sampler FontManager::m_sampler;
|
||||
@@ -57,6 +59,8 @@ const Font& FontManager::get(kFont id)
|
||||
}
|
||||
|
||||
bool TextMesh::create()
|
||||
{
|
||||
App::I.render_task([this]
|
||||
{
|
||||
glGenBuffers(2, font_buffers);
|
||||
#if USE_VBO
|
||||
@@ -70,6 +74,7 @@ bool TextMesh::create()
|
||||
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();
|
||||
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())
|
||||
{
|
||||
|
||||
167
src/main.cpp
167
src/main.cpp
@@ -33,12 +33,19 @@ std::thread::id gl_thread;
|
||||
std::map<kKey, int> 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<std::packaged_task<void()>> 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<std::packaged_task<void()>> 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<typename T, typename R = std::result_of<T()>::type>
|
||||
std::future<R> render_task_async(T task)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
extern std::deque<std::packaged_task<R()>> render_tasklist;
|
||||
extern std::mutex render_task_mutex;
|
||||
extern std::condition_variable render_cv;
|
||||
std::packaged_task<R()> pt(task);
|
||||
std::future<R> f = pt.get_future();
|
||||
if (is_render_thread())
|
||||
{
|
||||
pt();
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(render_task_mutex);
|
||||
render_tasklist.push_back(std::move(pt));
|
||||
}
|
||||
render_cv.notify_all();
|
||||
}
|
||||
return f;
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
template<typename T, typename R = std::result_of<T()>::type>
|
||||
R render_task(T task)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
extern std::deque<std::packaged_task<R()>> render_tasklist;
|
||||
extern std::mutex render_task_mutex;
|
||||
extern std::condition_variable render_cv;
|
||||
std::packaged_task<R()> pt(task);
|
||||
std::future<R> f = pt.get_future();
|
||||
if (is_render_thread())
|
||||
{
|
||||
pt();
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<std::mutex> _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<std::packaged_task<void()>> working_list;
|
||||
|
||||
// move the task list locally to free the queue for other threads
|
||||
{
|
||||
std::unique_lock<std::mutex> 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<std::mutex> 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<std::mutex> lock(render_mutex);
|
||||
std::unique_lock<std::mutex> 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();
|
||||
render_task([frame_timer]
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
App::I.clear();
|
||||
App::I.update(frame_timer);
|
||||
SwapBuffers(hDC);
|
||||
async_unlock();
|
||||
});
|
||||
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.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);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "node_colorwheel.h"
|
||||
#include "shader.h"
|
||||
#include "log.h"
|
||||
#include "app.h"
|
||||
|
||||
Node* NodeColorWheel::clone_instantiate() const
|
||||
{
|
||||
@@ -42,6 +43,8 @@ void NodeColorWheel::loaded()
|
||||
vertices.push_back({{glm::cos(2.f/3.f*glm::pi<float>())*l,glm::sin(2.f/3.f*glm::pi<float>())*l,0,1},{0,0},{0,0,0,1}});
|
||||
vertices.push_back({{l,0,0,1},{1,1},{1,0,0,1}});
|
||||
|
||||
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);
|
||||
@@ -57,6 +60,7 @@ void NodeColorWheel::loaded()
|
||||
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()
|
||||
|
||||
@@ -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);
|
||||
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
|
||||
|
||||
@@ -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,8 +528,8 @@ void NodeStrokePreview::draw_stroke()
|
||||
bool to_unload = (node->m_brush->m_tip_texture == nullptr);
|
||||
node->m_brush->preload();
|
||||
|
||||
node->async_start();
|
||||
|
||||
App::I.render_task([node, to_unload]
|
||||
{
|
||||
gl_state gl;
|
||||
gl.save();
|
||||
|
||||
@@ -553,20 +551,18 @@ void NodeStrokePreview::draw_stroke()
|
||||
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();
|
||||
|
||||
33
src/rtt.cpp
33
src/rtt.cpp
@@ -2,6 +2,7 @@
|
||||
#include "log.h"
|
||||
#include "rtt.h"
|
||||
#include "util.h"
|
||||
#include "app.h"
|
||||
|
||||
RTT::RTT()
|
||||
{
|
||||
@@ -38,6 +39,8 @@ void RTT::resize(int width, int height)
|
||||
{
|
||||
RTT new_rtt;
|
||||
|
||||
App::I.render_task([&]
|
||||
{
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldDFboID);
|
||||
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldRFboID);
|
||||
|
||||
@@ -51,6 +54,7 @@ void RTT::resize(int width, int height)
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, oldRFboID);
|
||||
|
||||
destroy();
|
||||
});
|
||||
|
||||
oldRFboID = 0;
|
||||
oldDFboID = 0;
|
||||
@@ -63,6 +67,8 @@ void RTT::resize(int width, int height)
|
||||
}
|
||||
|
||||
void RTT::destroy()
|
||||
{
|
||||
App::I.render_task_async([rboID=rboID, texID=texID, fboID=fboID]
|
||||
{
|
||||
if (rboID)
|
||||
{
|
||||
@@ -70,16 +76,17 @@ void RTT::destroy()
|
||||
}
|
||||
if (texID)
|
||||
{
|
||||
unbindTexture();
|
||||
//unbindTexture();
|
||||
glDeleteTextures(1, &texID);
|
||||
//LOG("TEX rtt destroy %d", texID)
|
||||
}
|
||||
if (fboID)
|
||||
{
|
||||
unbindFramebuffer();
|
||||
//unbindFramebuffer();
|
||||
glDeleteFramebuffers(1, &fboID);
|
||||
//LOG("RTT DESTROY %d", fboID);
|
||||
}
|
||||
});
|
||||
texID = 0;
|
||||
fboID = 0;
|
||||
rboID = 0;
|
||||
@@ -88,6 +95,8 @@ void RTT::destroy()
|
||||
}
|
||||
|
||||
void RTT::copy(const RTT & source)
|
||||
{
|
||||
App::I.render_task([&]
|
||||
{
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldDFboID);
|
||||
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldRFboID);
|
||||
@@ -98,9 +107,13 @@ void RTT::copy(const RTT & source)
|
||||
|
||||
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*/)
|
||||
{
|
||||
GLenum status = 0;
|
||||
App::I.render_task([&]
|
||||
{
|
||||
// Destroy any previously created object
|
||||
destroy();
|
||||
@@ -170,7 +183,7 @@ bool RTT::create(int width, int height, int tex/* = -1*/, GLint internal_format,
|
||||
};
|
||||
|
||||
// Check FBO status
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE)
|
||||
LOG("RTT::create failed because: %s", err2str(status));
|
||||
|
||||
@@ -178,12 +191,14 @@ bool RTT::create(int width, int height, int tex/* = -1*/, GLint internal_format,
|
||||
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();
|
||||
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();
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "log.h"
|
||||
#include "shader.h"
|
||||
#include "asset.h"
|
||||
#include "app.h"
|
||||
|
||||
std::map<kShader, Shader> ShaderManager::m_shaders;
|
||||
Shader* ShaderManager::m_current;
|
||||
@@ -152,6 +153,9 @@ bool Shader::reload()
|
||||
}
|
||||
|
||||
bool Shader::create(const char* vertex, const char* fragment)
|
||||
{
|
||||
bool ret = true;
|
||||
App::I.render_task([&]
|
||||
{
|
||||
GLint status;
|
||||
static char infolog[4096];
|
||||
@@ -161,7 +165,8 @@ bool Shader::create(const char* vertex, const char* fragment)
|
||||
auto vs = glCreateShader(GL_VERTEX_SHADER);
|
||||
if (!vs)
|
||||
{
|
||||
return false;
|
||||
ret = false;
|
||||
return;
|
||||
}
|
||||
source = vertex;
|
||||
glShaderSource(vs, 1, &source, nullptr);
|
||||
@@ -176,14 +181,16 @@ bool Shader::create(const char* vertex, const char* fragment)
|
||||
if (status == 0)
|
||||
{
|
||||
glDeleteShader(vs);
|
||||
return false;
|
||||
ret = false;
|
||||
return;
|
||||
}
|
||||
|
||||
auto fs = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
if (!fs)
|
||||
{
|
||||
glDeleteShader(vs);
|
||||
return false;
|
||||
ret = false;
|
||||
return;
|
||||
}
|
||||
source = fragment;
|
||||
glShaderSource(fs, 1, &source, nullptr);
|
||||
@@ -199,7 +206,8 @@ bool Shader::create(const char* vertex, const char* fragment)
|
||||
{
|
||||
glDeleteShader(vs);
|
||||
glDeleteShader(fs);
|
||||
return false;
|
||||
ret = false;
|
||||
return;
|
||||
}
|
||||
|
||||
auto ps = glCreateProgram();
|
||||
@@ -207,7 +215,8 @@ bool Shader::create(const char* vertex, const char* fragment)
|
||||
{
|
||||
glDeleteShader(vs);
|
||||
glDeleteShader(fs);
|
||||
return false;
|
||||
ret = false;
|
||||
return;
|
||||
}
|
||||
glAttachShader(ps, vs);
|
||||
glAttachShader(ps, fs);
|
||||
@@ -238,7 +247,8 @@ bool Shader::create(const char* vertex, const char* fragment)
|
||||
if (status == 0)
|
||||
{
|
||||
glDeleteProgram(ps);
|
||||
return false;
|
||||
ret = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse shader uniforms
|
||||
@@ -259,16 +269,20 @@ bool Shader::create(const char* vertex, const char* fragment)
|
||||
}
|
||||
|
||||
prog = ps;
|
||||
});
|
||||
|
||||
return true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Shader::destroy()
|
||||
{
|
||||
if (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)
|
||||
{
|
||||
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,6 +310,7 @@ void Shader::u_vec3(kShaderUniform id, const glm::vec3& v)
|
||||
|
||||
void Shader::u_vec2(kShaderUniform id, const glm::vec2& v)
|
||||
{
|
||||
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));
|
||||
@@ -300,12 +318,14 @@ void Shader::u_vec2(kShaderUniform id, const glm::vec2& v)
|
||||
|
||||
void Shader::u_mat4(kShaderUniform id, const glm::mat4& m)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,11 +21,17 @@ bool Shape::create_buffers_imp(GLvoid* idx, GLvoid* vertices, int isize, int vsi
|
||||
{
|
||||
use_idx = true;
|
||||
|
||||
bool ret = false;
|
||||
App::I.render_task([&]
|
||||
{
|
||||
destroy();
|
||||
|
||||
glGenBuffers(2, buffers);
|
||||
if (!(buffers[0] && buffers[1]))
|
||||
return false;
|
||||
{
|
||||
ret = false;
|
||||
return;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, isize, idx, GL_STATIC_DRAW);
|
||||
@@ -36,7 +43,10 @@ bool Shape::create_buffers_imp(GLvoid* idx, GLvoid* vertices, int isize, int vsi
|
||||
#if USE_VBO
|
||||
glGenVertexArrays(2, arrays);
|
||||
if (!(arrays[0] && arrays[1]))
|
||||
return false;
|
||||
{
|
||||
ret = false;
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
glBindVertexArray(arrays[i]);
|
||||
@@ -53,17 +63,24 @@ bool Shape::create_buffers_imp(GLvoid* idx, GLvoid* vertices, int isize, int vsi
|
||||
}
|
||||
glBindVertexArray(0);
|
||||
#endif
|
||||
return true;
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
bool Shape::create_buffers(GLvoid* vertices, int vsize)
|
||||
{
|
||||
use_idx = false;
|
||||
|
||||
bool ret = false;
|
||||
App::I.render_task([&]
|
||||
{
|
||||
destroy();
|
||||
|
||||
glGenBuffers(1, buffers);
|
||||
if (!buffers[0])
|
||||
return false;
|
||||
{
|
||||
ret = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (vsize)
|
||||
{
|
||||
@@ -76,7 +93,10 @@ bool Shape::create_buffers(GLvoid* vertices, int vsize)
|
||||
#if USE_VBO
|
||||
glGenVertexArrays(2, arrays);
|
||||
if (!(arrays[0] && arrays[1]))
|
||||
return false;
|
||||
{
|
||||
ret = false;
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
glBindVertexArray(arrays[i]);
|
||||
@@ -92,7 +112,8 @@ bool Shape::create_buffers(GLvoid* vertices, int vsize)
|
||||
}
|
||||
glBindVertexArray(0);
|
||||
#endif
|
||||
return true;
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
void Shape::draw_fill() const
|
||||
{
|
||||
@@ -103,6 +124,8 @@ void Shape::draw_fill() const
|
||||
type = GL_POINTS;
|
||||
if (count[0] == 2)
|
||||
type = GL_LINES;
|
||||
App::I.render_task([=]
|
||||
{
|
||||
#if USE_VBO
|
||||
glBindVertexArray(arrays[0]);
|
||||
if (use_idx)
|
||||
@@ -128,6 +151,7 @@ void Shape::draw_fill() const
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
#endif // USE_VBO
|
||||
});
|
||||
}
|
||||
void Shape::draw_stroke() const
|
||||
{
|
||||
@@ -136,6 +160,8 @@ void Shape::draw_stroke() const
|
||||
GLenum type = GL_LINES;
|
||||
if (count[1] == 1)
|
||||
type = GL_POINTS;
|
||||
App::I.render_task([=]
|
||||
{
|
||||
#if USE_VBO
|
||||
glBindVertexArray(arrays[1]);
|
||||
if (use_idx)
|
||||
@@ -162,6 +188,33 @@ void Shape::draw_stroke() const
|
||||
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,6 +490,8 @@ void Plane::update_vertices(const glm::vec4* data, const glm::vec2* uvs, const g
|
||||
vertices[i].pos.z = q;
|
||||
}
|
||||
|
||||
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]{
|
||||
@@ -452,6 +507,7 @@ void Plane::update_vertices(const glm::vec4* data, const glm::vec2* uvs, const g
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
});
|
||||
}
|
||||
void Circle::create_impl(float radius, int div, GLushort* idx, vertex_t* vertices)
|
||||
{
|
||||
@@ -703,6 +759,8 @@ void Sphere::create_impl(int rings, int sectors, float radius,
|
||||
}
|
||||
}
|
||||
void LineSegment::update_vertices(const glm::vec4 data[2])
|
||||
{
|
||||
App::I.render_task([&]
|
||||
{
|
||||
static vertex_t vertices[2];
|
||||
vertices[0] = { data[0], { 0, 0 } }; // A
|
||||
@@ -710,12 +768,16 @@ void LineSegment::update_vertices(const glm::vec4 data[2])
|
||||
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)
|
||||
{
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
17
src/shape.h
17
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)
|
||||
{
|
||||
|
||||
@@ -40,6 +40,8 @@ void TextureManager::invalidate()
|
||||
}
|
||||
|
||||
bool Texture2D::create(int width, int height, GLint internal_format, GLint format, const uint8_t* data)
|
||||
{
|
||||
App::I.render_task([=]
|
||||
{
|
||||
destroy();
|
||||
m_width = width;
|
||||
@@ -54,6 +56,7 @@ bool Texture2D::create(int width, int height, GLint internal_format, GLint forma
|
||||
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)
|
||||
@@ -64,11 +67,14 @@ bool Texture2D::create(const Image& img)
|
||||
}
|
||||
|
||||
void Texture2D::create_mipmaps()
|
||||
{
|
||||
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)
|
||||
{
|
||||
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
|
||||
@@ -112,25 +145,36 @@ glm::vec2 Texture2D::size() const
|
||||
Texture2D::~Texture2D()
|
||||
{
|
||||
if (auto_destroy)
|
||||
{
|
||||
App::I.render_task_async([this]
|
||||
{
|
||||
LOG("Texture2D auto destroy");
|
||||
App::I.async_start();
|
||||
destroy();
|
||||
App::I.async_end();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
#endif // USE_SAMPLER
|
||||
if (id == 0)
|
||||
return false;
|
||||
{
|
||||
ret = false;
|
||||
return;
|
||||
}
|
||||
set(filter, wrap);
|
||||
return true;
|
||||
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);
|
||||
@@ -139,31 +183,44 @@ void Sampler::set(GLint filter /*= GL_LINEAR*/, GLint wrap /*= GL_CLAMP_TO_EDGE*
|
||||
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);
|
||||
#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));
|
||||
#endif // USE_SAMPLER
|
||||
});
|
||||
}
|
||||
void Sampler::bind(int unit) const
|
||||
{
|
||||
App::I.render_task([=]
|
||||
{
|
||||
current_unit = unit;
|
||||
#if USE_SAMPLER
|
||||
glBindSampler(unit, id);
|
||||
#endif // USE_SAMPLER
|
||||
});
|
||||
}
|
||||
void Sampler::unbind()
|
||||
{
|
||||
App::I.render_task([=]
|
||||
{
|
||||
#if USE_SAMPLER
|
||||
glBindSampler(current_unit, 0);
|
||||
#endif // USE_SAMPLER
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user