render implement thread, wrap GL commands into tasks

This commit is contained in:
2019-07-06 22:25:07 +02:00
parent db27334ce5
commit 0012e2ce9b
18 changed files with 1252 additions and 904 deletions

View File

@@ -53,14 +53,12 @@ void App::open_document(std::string path)
// on complete // on complete
if (success) if (success)
{ {
async_start();
title_update(); title_update();
for (int layer_index = 0; layer_index < canvas->m_canvas->m_layers.size(); layer_index++) 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); 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); l->m_visibility->set_value(canvas->m_canvas->m_layers[layer_index]->m_visible);
} }
async_end();
} }
else else
{ {
@@ -80,7 +78,6 @@ bool App::request_close()
return true; return true;
if (!dialog_already_opened) if (!dialog_already_opened)
{ {
async_start();
auto* m = layout[main_id]->add_child<NodeMessageBox>(); auto* m = layout[main_id]->add_child<NodeMessageBox>();
m->m_title->set_text("Unsaved document"); m->m_title->set_text("Unsaved document");
m->m_message->set_text("Do you want to close without saving?"); m->m_message->set_text("Do you want to close without saving?");
@@ -100,8 +97,6 @@ bool App::request_close()
m->destroy(); m->destroy();
dialog_already_opened = false; dialog_already_opened = false;
}; };
async_redraw();
async_end();
dialog_already_opened = true; dialog_already_opened = true;
} }
return false; return false;
@@ -109,6 +104,7 @@ bool App::request_close()
void App::clear() void App::clear()
{ {
assert(is_render_thread());
glClearColor(.1f, .1f, .1f, 1.f); glClearColor(.1f, .1f, .1f, 1.f);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
} }
@@ -384,67 +380,75 @@ void App::upload(std::string filename, std::string name, std::function<void(floa
} }
} }
#ifdef _WIN32
static CONSOLE_SCREEN_BUFFER_INFO info;
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 = {
{ 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() void App::init()
{ {
#ifdef _WIN32 #ifdef _WIN32
if (glDebugMessageCallback) 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 // colors: http://stackoverflow.com/questions/4053837/colorizing-text-in-the-console-with-c
glDebugMessageCallback([](GLenum source, GLenum type, GLuint id, GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info);
GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
render_task([]
{ {
static std::map<GLenum, int> colors = { glDebugMessageCallback(handle_gl_callback, nullptr);
{ GL_DEBUG_SEVERITY_NOTIFICATION, 8 }, glEnable(GL_DEBUG_OUTPUT);
{ GL_DEBUG_SEVERITY_LOW, 8 }, glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
{ 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);
} }
#endif #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); LOG("Screen Resolution: %dx%d", (int)width, (int)height);
//zoom = ceilf(width / 2000.f); render_task([]
//zoom = 2; {
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); glDisable(GL_DEPTH_TEST);
#if defined(_WIN32) || defined(__OSX__) #if defined(_WIN32) || defined(__OSX__)
glEnable(GL_PROGRAM_POINT_SIZE); glEnable(GL_PROGRAM_POINT_SIZE);
glEnable(GL_LINE_SMOOTH); glEnable(GL_LINE_SMOOTH);
#endif #endif
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquationSeparate(GL_FUNC_ADD, GL_MAX); glBlendEquationSeparate(GL_FUNC_ADD, GL_MAX);
});
int run_counter = Settings::value<Serializer::Integer>("run_counter") + 1; int run_counter = Settings::value<Serializer::Integer>("run_counter") + 1;
Settings::set("run_counter", Serializer::Integer(run_counter)); 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."); 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() void App::async_start()
@@ -700,7 +695,6 @@ void App::terminate()
NodeStrokePreview::terminate_renderer(); NodeStrokePreview::terminate_renderer();
rec_stop(); rec_stop();
async_start();
TextureManager::invalidate(); TextureManager::invalidate();
ShaderManager::invalidate(); ShaderManager::invalidate();
layout.unload(); layout.unload();
@@ -716,7 +710,6 @@ void App::terminate()
floating_layers.reset(); floating_layers.reset();
floating_picker.reset(); floating_picker.reset();
quick_mode_state.clear(); quick_mode_state.clear();
async_end();
} }
void App::update_memory_usage(size_t bytes) void App::update_memory_usage(size_t bytes)

View File

@@ -249,4 +249,69 @@ public:
bool get_ui_rtl() const; bool get_ui_rtl() const;
void cmd_convert(std::string pano_path, std::string out_path); 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([] {});
}
}; };

View File

@@ -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) 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>(); auto* m = layout[main_id]->add_child<NodeMessageBox>();
m->m_title->set_text(title.c_str()); m->m_title->set_text(title.c_str());
m->m_message->set_text(text.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) if (!cancel_button)
m->btn_cancel->destroy(); m->btn_cancel->destroy();
layout[main_id]->update(); layout[main_id]->update();
async_redraw();
async_end();
return m; return m;
} }
@@ -80,7 +77,6 @@ void App::dialog_about()
void App::dialog_newdoc() void App::dialog_newdoc()
{ {
auto show_dialog = [this] { auto show_dialog = [this] {
async_start();
auto dialog = std::make_shared<NodeDialogNewDoc>(); auto dialog = std::make_shared<NodeDialogNewDoc>();
dialog->m_manager = &layout; dialog->m_manager = &layout;
dialog->init(); dialog->init();
@@ -153,7 +149,6 @@ void App::dialog_newdoc()
dialog->destroy(); dialog->destroy();
App::I.hideKeyboard(); App::I.hideKeyboard();
}; };
async_end();
}; };
if (canvas) if (canvas)
@@ -190,7 +185,6 @@ void App::dialog_newdoc()
void App::dialog_open() void App::dialog_open()
{ {
auto show_dialog = [this] { auto show_dialog = [this] {
async_start();
// load thumbnail test // load thumbnail test
auto dialog = std::make_shared<NodeDialogOpen>(); auto dialog = std::make_shared<NodeDialogOpen>();
dialog->m_manager = &layout; dialog->m_manager = &layout;
@@ -217,7 +211,6 @@ void App::dialog_open()
// dialog->destroy(); // dialog->destroy();
// ActionManager::clear(); // ActionManager::clear();
}; };
async_end();
}; };
if (canvas) if (canvas)
@@ -253,7 +246,6 @@ void App::dialog_open()
void App::dialog_browse() void App::dialog_browse()
{ {
auto show_dialog = [this] { auto show_dialog = [this] {
async_start();
// load thumbnail test // load thumbnail test
auto dialog = std::make_shared<NodeDialogBrowse>(); auto dialog = std::make_shared<NodeDialogBrowse>();
dialog->m_manager = &layout; dialog->m_manager = &layout;
@@ -277,7 +269,6 @@ void App::dialog_browse()
dialog->destroy(); dialog->destroy();
} }
}; };
async_end();
}; };
if (canvas) if (canvas)

View File

@@ -9,16 +9,18 @@ void App::initShaders()
std::logic_error("check_uniform_uniqueness() failed"); std::logic_error("check_uniform_uniqueness() failed");
#endif // _DEBUG #endif // _DEBUG
GLint n_exts; render_task([] {
glGetIntegerv(GL_NUM_EXTENSIONS, &n_exts); GLint n_exts;
for (int i = 0; i < n_exts; i++) 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)
{ {
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"); LOG("Shader Extension shader_framebuffer_fetch: %s", ShaderManager::ext_framebuffer_fetch ? "enabled" : "disabled");

View File

@@ -6,6 +6,7 @@
void BrushMesh::draw(const std::vector<StrokeSample>& samples, const glm::mat4& proj) void BrushMesh::draw(const std::vector<StrokeSample>& samples, const glm::mat4& proj)
{ {
assert(App::I.is_render_thread());
std::vector<instance_t> attributes; std::vector<instance_t> attributes;
attributes.reserve(samples.size()); attributes.reserve(samples.size());
for (const auto& s : samples) for (const auto& s : samples)
@@ -69,64 +70,74 @@ void BrushMesh::draw(const std::vector<StrokeSample>& samples, const glm::mat4&
} }
bool BrushMesh::create() bool BrushMesh::create()
{ {
static GLushort idx[6]{ 0, 1, 2, 0, 2, 3 }; bool ret = true;
static vertex_t vertices[4]{ App::I.render_task([&]
{ { -.5f, -.5f, 0, 1 }, { 0, 0 } }, // A B----C {
{ { -.5f, .5f, 0, 1 }, { 0, 1 } }, // B --\ | | static GLushort idx[6]{ 0, 1, 2, 0, 2, 3 };
{ { .5f, .5f, 0, 1 }, { 1, 1 } }, // C --/ | | static vertex_t vertices[4]{
{ { .5f, -.5f, 0, 1 }, { 1, 0 } }, // D A----D { { -.5f, -.5f, 0, 1 }, { 0, 0 } }, // A B----C
}; { { -.5f, .5f, 0, 1 }, { 0, 1 } }, // B --\ | |
glGenBuffers(3, buffers); { { .5f, .5f, 0, 1 }, { 1, 1 } }, // C --/ | |
if (!(buffers[0] && buffers[1] && buffers[2])) { { .5f, -.5f, 0, 1 }, { 1, 0 } }, // D A----D
return false; };
glGenBuffers(3, buffers);
if (!(buffers[0] && buffers[1] && buffers[2]))
{
ret = false;
return;
}
static instance_t inst{ glm::mat4(1), .1f }; static instance_t inst{ glm::mat4(1), .1f };
glBindBuffer(GL_ARRAY_BUFFER, buffers[2]); glBindBuffer(GL_ARRAY_BUFFER, buffers[2]);
glBufferData(GL_ARRAY_BUFFER, sizeof(instance_t), &inst, GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, sizeof(instance_t), &inst, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(idx), idx, GL_STATIC_DRAW); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(idx), idx, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_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_flow = shader->GetAttribLocation("a_flow");
loc_mvp = shader->GetAttribLocation("a_mvp"); loc_mvp = shader->GetAttribLocation("a_mvp");
#if USE_VBO #if USE_VBO
glGenVertexArrays(1, &vao); glGenVertexArrays(1, &vao);
if (!vao) if (!vao)
return false; {
glBindVertexArray(vao); ret = false;
glEnableVertexAttribArray(0); return;
glEnableVertexAttribArray(1); }
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, pos)); glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs)); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
glBindBuffer(GL_ARRAY_BUFFER, buffers[2]); glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
// Loop over each column of the matrix... glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, pos));
for (int i = 0; i < 4; i++) glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs));
{ glBindBuffer(GL_ARRAY_BUFFER, buffers[2]);
// Set up the vertex attribute // Loop over each column of the matrix...
glVertexAttribPointer(loc_mvp + i, 4, GL_FLOAT, GL_FALSE, sizeof(instance_t), for (int i = 0; i < 4; i++)
(GLvoid*)(offsetof(instance_t, mvp) + sizeof(glm::vec4) * i)); {
// Enable it // Set up the vertex attribute
glEnableVertexAttribArray(loc_mvp + i); glVertexAttribPointer(loc_mvp + i, 4, GL_FLOAT, GL_FALSE, sizeof(instance_t),
// Make it instanced (GLvoid*)(offsetof(instance_t, mvp) + sizeof(glm::vec4) * i));
glVertexAttribDivisor(loc_mvp + i, 1); // Enable it
} glEnableVertexAttribArray(loc_mvp + i);
glEnableVertexAttribArray(loc_flow); // Make it instanced
glVertexAttribPointer(loc_flow, 1, GL_FLOAT, GL_FALSE, sizeof(instance_t), glVertexAttribDivisor(loc_mvp + i, 1);
(GLvoid*)offsetof(instance_t, flow)); }
glVertexAttribDivisor(loc_flow, 1); glEnableVertexAttribArray(loc_flow);
glBindVertexArray(0); glVertexAttribPointer(loc_flow, 1, GL_FLOAT, GL_FALSE, sizeof(instance_t),
(GLvoid*)offsetof(instance_t, flow));
glVertexAttribDivisor(loc_flow, 1);
glBindVertexArray(0);
#endif #endif
});
return true; return ret;
} }
StrokeSample Stroke::randomize_sample(const glm::vec3& pos, float pressure, float dir_angle) StrokeSample Stroke::randomize_sample(const glm::vec3& pos, float pressure, float dir_angle)
{ {

View File

@@ -1474,10 +1474,13 @@ void Canvas::FloodData::apply()
if (!dirty[plane]) if (!dirty[plane])
continue; continue;
auto& rtt = layer->m_rtt[plane]; auto& rtt = layer->m_rtt[plane];
rtt.bindTexture(); App::I.render_task([&]
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rtt.getWidth(), rtt.getHeight(), {
GL_RGBA, GL_UNSIGNED_BYTE, rgb[plane].get()); rtt.bindTexture();
rtt.unbindTexture(); 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; 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) Image Canvas::thumbnail_generate(int w, int h)
{ {
// save viewport and clear color states Image image;
GLint vp[4]; image.create(w, h);
GLfloat cc[4];
glGetIntegerv(GL_VIEWPORT, vp);
glGetFloatv(GL_COLOR_CLEAR_VALUE, cc);
GLboolean blend = glIsEnabled(GL_BLEND);
// prepare common states App::I.render_task([this, w, h, &image]
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++)
{ {
glDisable(GL_BLEND); // save viewport and clear color states
auto plane_mvp = proj * m_mv * m_plane_transform[i] * glm::translate(glm::vec3(0, 0, -1)); 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); // prepare common states
ShaderManager::u_int(kShaderUniform::Tex, 0); glViewport(0, 0, w, h);
ShaderManager::u_int(kShaderUniform::TexA, 1);
ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp); RTT fb;
if (!ShaderManager::ext_framebuffer_fetch) 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); glDisable(GL_BLEND);
glActiveTexture(GL_TEXTURE2); auto plane_mvp = proj * m_mv * m_plane_transform[i] * glm::translate(glm::vec3(0, 0, -1));
blendtex.bind();
m_sampler_bg.bind(2); ShaderManager::use(kShader::TextureBlend);
} ShaderManager::u_int(kShaderUniform::Tex, 0);
m_sampler_bg.bind(0); // nearest ShaderManager::u_int(kShaderUniform::TexA, 1);
m_sampler_mask.bind(1); // linear ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp);
for (int layer_index = 0; layer_index < m_layers.size(); layer_index++) if (!ShaderManager::ext_framebuffer_fetch)
{ {
if (!m_layers[layer_index]->m_visible || ShaderManager::u_int(kShaderUniform::TexBG, 2);
m_layers[layer_index]->m_opacity == 0.f || glActiveTexture(GL_TEXTURE2);
!m_layers[layer_index]->m_dirty_face[i]) blendtex.bind();
continue; 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) if (!ShaderManager::ext_framebuffer_fetch)
{ {
glActiveTexture(GL_TEXTURE2); 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_TEXTURE0);
{ blendtex.bind();
glActiveTexture(GL_TEXTURE2); // 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(); 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); 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; return image;
} }
@@ -2904,140 +2911,146 @@ 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) void Canvas::draw_objects_direct(std::function<void(const glm::mat4& camera, const glm::mat4& proj, int i)> observer, Layer& layer)
{ {
// save viewport and clear color states App::I.render_task([&]
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++)
{ {
glm::mat4 plane_camera = glm::lookAt(glm::vec3(0), m_plane_origin[i], m_plane_tangent[i]); // save viewport and clear color states
layer.m_rtt[i].bindFramebuffer(); GLint vp[4];
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboID); 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); GLuint rboID;
layer.m_rtt[i].unbindFramebuffer(); 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; glm::mat4 proj = glm::perspective(glm::radians(90.f), 1.f, .01f, 1000.f);
layer.m_dirty_box[i] = { 0, 0, layer.w, layer.h }; 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 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND); layer.m_rtt[i].unbindFramebuffer();
glViewport(vp[0], vp[1], vp[2], vp[3]);
glClearColor(cc[0], cc[1], cc[2], cc[3]);
glActiveTexture(GL_TEXTURE0);
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<void(const glm::mat4& camera, const glm::mat4& proj, int i)> observer, Layer& layer) void Canvas::draw_objects(std::function<void(const glm::mat4& camera, const glm::mat4& proj, int i)> observer, Layer& layer)
{ {
// save viewport and clear color states App::I.render_task([&]
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++)
{ {
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.bindFramebuffer();
rtt.clear({ 1, 1, 1, 0 }); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboID);
observer(plane_camera, proj, i);
rtt.unbindFramebuffer(); 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(); glm::mat4 proj = glm::perspective(glm::radians(90.f), 1.f, .01f, 1000.f);
for (int i = 0; i < 6; i++)
// 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<uint8_t[]>(box_sz.x * box_sz.y * 4); glm::mat4 plane_camera = glm::lookAt(glm::vec3(0), m_plane_origin[i], m_plane_tangent[i]);
glReadPixels(bounds.x, bounds.y, box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, action->m_image[i].get()); rtt.bindFramebuffer();
action->m_box[i] = bounds; 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<uint8_t[]>(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]; // save history
action->m_old_dirty[i] = layer.m_dirty_face[i]; 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 glDeleteRenderbuffers(1, &rboID);
if (has_data) rtt.destroy();
{
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(); // 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 draw_merge();
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();
} }
void Canvas::draw_objects(std::function<void(const glm::mat4& camera, const glm::mat4& proj, int i)> observer) 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() void Layer::optimize()
{ {
int saved_bytes = 0; int saved_bytes = 0;
glBindTexture(GL_TEXTURE_2D, 0);
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
if (!m_dirty_face[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!! // it's just a quick fix DON'T SHIP!!
//m_rtt[i].recreate(); //m_rtt[i].recreate();
m_rtt[i].bindTexture(); App::I.render_task_async([this,i,&snap]
glm::vec2 box_sz = zw(m_dirty_box[i]) - xy(m_dirty_box[i]); {
glTexSubImage2D(GL_TEXTURE_2D, 0, m_rtt[i].bindTexture();
m_dirty_box[i].x, m_dirty_box[i].y, glm::vec2 box_sz = zw(m_dirty_box[i]) - xy(m_dirty_box[i]);
box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, glTexSubImage2D(GL_TEXTURE_2D, 0,
snap.image[i].get()); m_dirty_box[i].x, m_dirty_box[i].y,
m_rtt[i].unbindTexture(); box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE,
LOG("restore face %d - %d bytes (%dx%d)", i, snap.image[i].get());
(int)box_sz.x * (int)box_sz.y * 4, (int)box_sz.x, (int)box_sz.y); 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*/) Layer::Snapshot Layer::snapshot(std::array<glm::vec4, 6> * dirty_box /*= nullptr*/, std::array<bool, 6> * dirty_face /*= nullptr*/)
{ {
Snapshot snap; Snapshot snap;
static int counter = 0;
glBindTexture(GL_TEXTURE_2D, 0);
//glBindFramebuffer(GL_FRAMEBUFFER, 0);
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
snap.m_dirty_box[i] = dirty_box ? dirty_box->at(i) : m_dirty_box[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<glm::vec4, 6> * dirty_box /*= nullptr
snap.image[i] = std::make_unique<uint8_t[]>(m_rtt[i].bytes()); snap.image[i] = std::make_unique<uint8_t[]>(m_rtt[i].bytes());
//glReadBuffer(GL_BACK); //glReadBuffer(GL_BACK);
m_rtt[i].bindFramebuffer(); App::I.render_task_async([this,i,&snap]
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].bindFramebuffer();
m_rtt[i].unbindFramebuffer(); 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); //glReadBuffer(GL_NONE);
} }
counter++; App::I.render_sync();
return snap; return snap;
} }
void Layer::clear(const glm::vec4& c) void Layer::clear(const glm::vec4& c)
{ {
// push clear color state App::I.render_task([&]
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++)
{ {
m_rtt[i].bindFramebuffer(); // push clear color state
glClear(GL_COLOR_BUFFER_BIT); GLfloat cc[4];
m_rtt[i].unbindFramebuffer(); glGetFloatv(GL_COLOR_CLEAR_VALUE, cc);
glClearColor(c.r, c.g, c.b, c.a);
if (erase) bool erase = (c.a == 0.f);
{
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 for (int i = 0; i < 6; i++)
glClearColor(cc[0], cc[1], cc[2], cc[3]); {
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) 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; m_name = name;
w = width; w = width;
h = height; h = height;
for (int i = 0; i < 6; i++) App::I.render_task([&]
{ {
m_rtt[i].create(width, height); for (int i = 0; i < 6; i++)
m_rtt[i].bindFramebuffer(); {
m_rtt[i].clear(); m_rtt[i].create(width, height);
m_rtt[i].unbindFramebuffer(); m_rtt[i].bindFramebuffer();
m_dirty_box[i] = glm::vec4(w, h, 0, 0); // reset bounding box m_rtt[i].clear();
m_dirty_face[i] = false; m_rtt[i].unbindFramebuffer();
} m_dirty_box[i] = glm::vec4(w, h, 0, 0); // reset bounding box
m_dirty_face[i] = false;
}
});
return true; return true;
} }

View File

@@ -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) 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) if (m_draw_tip)
{ {
const auto& brush = Canvas::I->m_current_brush; const auto& brush = Canvas::I->m_current_brush;

View File

@@ -3,6 +3,8 @@
#include "font.h" #include "font.h"
#include "shader.h" #include "shader.h"
#include "asset.h" #include "asset.h"
#include "util.h"
#include "app.h"
std::map<kFont, Font> FontManager::m_fonts; std::map<kFont, Font> FontManager::m_fonts;
Sampler FontManager::m_sampler; Sampler FontManager::m_sampler;
@@ -58,18 +60,21 @@ const Font& FontManager::get(kFont id)
bool TextMesh::create() bool TextMesh::create()
{ {
glGenBuffers(2, font_buffers); App::I.render_task([this]
{
glGenBuffers(2, font_buffers);
#if USE_VBO #if USE_VBO
glGenVertexArrays(1, &font_array); glGenVertexArrays(1, &font_array);
glBindVertexArray(font_array); glBindVertexArray(font_array);
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, font_buffers[1]); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, font_buffers[1]);
glBindBuffer(GL_ARRAY_BUFFER, font_buffers[0]); glBindBuffer(GL_ARRAY_BUFFER, font_buffers[0]);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (GLvoid*)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)); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (GLvoid*)(sizeof(float) * 2));
glBindVertexArray(0); glBindVertexArray(0);
#endif // USE_VBO #endif // USE_VBO
});
return true; return true;
} }
@@ -120,17 +125,21 @@ void TextMesh::update(kFont id, const char* text)
vi -= glm::vec4(bbmin, 0, 0); vi -= glm::vec4(bbmin, 0, 0);
bb = bbmax - bbmin; bb = bbmax - bbmin;
font_array_count = (int)idx.size(); font_array_count = (int)idx.size();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, font_buffers[1]); App::I.render_task([&]
glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx.size() * sizeof(GLushort), idx.data(), GL_STATIC_DRAW); {
glBindBuffer(GL_ARRAY_BUFFER, font_buffers[0]); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, font_buffers[1]);
glBufferData(GL_ARRAY_BUFFER, v.size() * sizeof(glm::vec4), v.data(), GL_STATIC_DRAW); glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx.size() * sizeof(GLushort), idx.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, font_buffers[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 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() void TextMesh::draw()
{ {
assert(App::I.is_render_thread());
auto& f = FontManager::get(font_id); auto& f = FontManager::get(font_id);
if (f.font_tex.ready()) if (f.font_tex.ready())
{ {

View File

@@ -33,12 +33,19 @@ std::thread::id gl_thread;
std::map<kKey, int> vkey_map; std::map<kKey, int> vkey_map;
std::thread hmd_renderer; std::thread hmd_renderer;
std::thread renderer; std::thread ui_renderer;
int vr_frames = 0; int vr_frames = 0;
int running = -1; int running = -1;
int vr_running = 0; 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::condition_variable render_cv;
std::thread render_thread;
std::thread::id render_thread_id;
bool render_running = false;
int gl_count = 0; int gl_count = 0;
std::deque<std::packaged_task<void()>> tasklist; 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() void async_lock()
{ {
//std::lock_guard<std::mutex> _lock(async_mutex); //std::lock_guard<std::mutex> _lock(async_mutex);
@@ -166,7 +230,7 @@ void async_unlock()
void win32_render_thread_notify() void win32_render_thread_notify()
{ {
render_cv.notify_all(); ui_render_cv.notify_all();
} }
void win32_show_cursor(bool visible) void win32_show_cursor(bool visible)
@@ -257,12 +321,6 @@ std::string win32_open_dir()
return Buffer; return Buffer;
} }
struct async_locker
{
async_locker() { async_lock(); }
~async_locker() { async_unlock(); }
};
int read_WMI_info() int read_WMI_info()
{ {
// see: http://win32easy.blogspot.co.uk/2011/03/wmi-in-c-query-everyting-from-your-os.html // 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 = new Vive;
vive->on_draw = [](const glm::mat4& proj, const glm::mat4& view, const glm::mat4& pose) { App::I.vr_draw(proj, view, pose); }; 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()) if (!vive->Initialize())
{ {
delete vive; delete vive;
vive = nullptr; vive = nullptr;
LOG("VR: failed to initialize vive"); LOG("VR: failed to initialize vive");
async_unlock();
return false; return false;
} }
async_unlock();
hmd_renderer = std::thread([&] { hmd_renderer = std::thread([&] {
if (!vive) if (!vive)
@@ -726,6 +781,46 @@ BOOL UnadjustWindowRectEx(LPRECT prc, DWORD dwStyle, BOOL fMenu, DWORD dwExStyle
return fRc; 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) int main(int argc, char** argv)
{ {
WNDCLASS wc; WNDCLASS wc;
@@ -917,7 +1012,10 @@ int main(int argc, char** argv)
LOG("RegisterTouchWindow error: %s", GetLastErrorAsString().c_str()); LOG("RegisterTouchWindow error: %s", GetLastErrorAsString().c_str());
} }
async_lock(); wglMakeCurrent(NULL, NULL);
running = 1;
render_thread = std::thread(render_thread_main);
LOG("init app"); LOG("init app");
App::I.init(); App::I.init();
@@ -932,15 +1030,11 @@ int main(int argc, char** argv)
LOG("SKIP init WinTab"); LOG("SKIP init WinTab");
} }
async_unlock();
LOG("change icon"); LOG("change icon");
SendMessage(hWnd, WM_SETICON, ICON_SMALL, SendMessage(hWnd, WM_SETICON, ICON_SMALL,
(LPARAM)LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON1))); (LPARAM)LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON1)));
running = 1; ui_renderer = std::thread([&] {
renderer = std::thread([&] {
BT_SetTerminate(); BT_SetTerminate();
LOG("start render thread"); LOG("start render thread");
const float target_fps = 10; const float target_fps = 10;
@@ -989,13 +1083,11 @@ int main(int argc, char** argv)
if (!working_list.empty()) if (!working_list.empty())
{ {
async_lock();
while (!working_list.empty()) while (!working_list.empty())
{ {
working_list.front()(); working_list.front()();
working_list.pop_front(); working_list.pop_front();
} }
async_unlock();
//LOG("clear"); //LOG("clear");
//WacomTablet::I.m_stylus = false; //WacomTablet::I.m_stylus = false;
//WacomTablet::I.m_eraser = false; //WacomTablet::I.m_eraser = false;
@@ -1016,7 +1108,7 @@ int main(int argc, char** argv)
App::I.tick(dt); 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) if (render_timer > 1.0f / target_fps)
{ {
App::I.redraw = true; App::I.redraw = true;
@@ -1041,19 +1133,20 @@ int main(int argc, char** argv)
if (App::I.redraw) if (App::I.redraw)
{ {
async_lock(); render_task([frame_timer]
glBindFramebuffer(GL_FRAMEBUFFER, 0); {
App::I.clear(); glBindFramebuffer(GL_FRAMEBUFFER, 0);
App::I.update(frame_timer); App::I.clear();
SwapBuffers(hDC); App::I.update(frame_timer);
async_unlock(); SwapBuffers(hDC);
});
frame_timer = 0; frame_timer = 0;
frames++; frames++;
} }
const int framerate = (1.f / target_tick_rate) * 1000; const int framerate = (1.f / target_tick_rate) * 1000;
const int diff = framerate - (t1 - t0); 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)); //std::this_thread::sleep_for(std::chrono::milliseconds(30));
t0 = t1; t0 = t1;
} }
@@ -1109,10 +1202,11 @@ int main(int argc, char** argv)
} }
if (!tasklist.empty()) if (!tasklist.empty())
render_cv.notify_all(); ui_render_cv.notify_all();
} }
// Clean up // Clean up
WacomTablet::I.terminate(); WacomTablet::I.terminate();
render_cv.notify_all();
UnregisterClass(className, hInst); UnregisterClass(className, hInst);
LogRemote::I.stop(); LogRemote::I.stop();
@@ -1132,13 +1226,16 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{ {
case WM_USER_CLOSE: case WM_USER_CLOSE:
running = 0; running = 0;
render_cv.notify_all(); ui_render_cv.notify_all();
if (renderer.joinable()) if (ui_renderer.joinable())
renderer.join(); ui_renderer.join();
if (hmd_renderer.joinable()) if (hmd_renderer.joinable())
hmd_renderer.join(); hmd_renderer.join();
App::I.terminate(); App::I.terminate();
PostQuitMessage(0); PostQuitMessage(0);
render_running = false;
if (render_thread.joinable())
render_thread.join();
return 0; return 0;
case WM_PAINT: case WM_PAINT:
App::I.redraw = true; App::I.redraw = true;
@@ -1150,13 +1247,16 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
if (App::I.request_close()) if (App::I.request_close())
{ {
running = 0; running = 0;
render_cv.notify_all(); ui_render_cv.notify_all();
if (renderer.joinable()) if (ui_renderer.joinable())
renderer.join(); ui_renderer.join();
if (hmd_renderer.joinable()) if (hmd_renderer.joinable())
hmd_renderer.join(); hmd_renderer.join();
App::I.terminate(); App::I.terminate();
PostQuitMessage(0); PostQuitMessage(0);
render_running = false;
if (render_thread.joinable())
render_thread.join();
return 0; return 0;
} }
else else
@@ -1170,12 +1270,14 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
auto h = (float)HIWORD(lp); auto h = (float)HIWORD(lp);
if (h != 0 && running == 1) if (h != 0 && running == 1)
{ {
async_locker lock; App::I.render_task([=]
App::I.resize(w, h); {
App::I.clear(); App::I.resize(w, h);
App::I.redraw = true; App::I.clear();
App::I.update(0.f); App::I.redraw = true;
SwapBuffers(hDC); App::I.update(0.f);
SwapBuffers(hDC);
});
} }
break; break;
} }
@@ -1374,7 +1476,6 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
break; break;
case WM_MOUSEWHEEL: case WM_MOUSEWHEEL:
{ {
async_locker lock;
POINT pt; POINT pt;
pt.x = GET_X_LPARAM(lp); pt.x = GET_X_LPARAM(lp);
pt.y = GET_Y_LPARAM(lp); pt.y = GET_Y_LPARAM(lp);

View File

@@ -2,6 +2,7 @@
#include "node_colorwheel.h" #include "node_colorwheel.h"
#include "shader.h" #include "shader.h"
#include "log.h" #include "log.h"
#include "app.h"
Node* NodeColorWheel::clone_instantiate() const Node* NodeColorWheel::clone_instantiate() const
{ {
@@ -42,21 +43,24 @@ 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({{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}}); vertices.push_back({{l,0,0,1},{1,1},{1,0,0,1}});
glGenBuffers(1, &buffers); App::I.render_task([&]
glBindBuffer(GL_ARRAY_BUFFER, buffers); {
glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(vertex_t), vertices.data(), GL_STATIC_DRAW); glGenBuffers(1, &buffers);
glBindBuffer(GL_ARRAY_BUFFER, 0); 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); glGenVertexArrays(1, &arrays);
glBindVertexArray(arrays); glBindVertexArray(arrays);
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2); glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, buffers); glBindBuffer(GL_ARRAY_BUFFER, buffers);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)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(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)); glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, col));
glBindVertexArray(0); glBindVertexArray(0);
});
} }
void NodeColorWheel::draw() void NodeColorWheel::draw()

View File

@@ -151,11 +151,14 @@ void NodePanelGrid::init_controls()
m_texture.create_mipmaps(); m_texture.create_mipmaps();
#else #else
// get the texture data and resize it // get the texture data and resize it
m_texture.bind();
Image img; Image img;
img.create(m_texture.size().x, m_texture.size().y); img.create(m_texture.size().x, m_texture.size().y);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.m_data.get()); App::I.render_task([&]
m_texture.unbind(); {
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); Image resized = img.resize(texres, texres);
m_texture.create(resized); m_texture.create(resized);
m_texture.create_mipmaps(); m_texture.create_mipmaps();
@@ -169,9 +172,6 @@ void NodePanelGrid::init_controls()
m_plane.create<1>(1, 1); m_plane.create<1>(1, 1);
TextureManager::load("data/sun.png"); 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 int NodePanelGrid::get_samples() const

View File

@@ -514,13 +514,11 @@ void NodeStrokePreview::draw_stroke()
// Good luck, future Omar // Good luck, future Omar
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));
#endif #endif
App::I.async_start();
m_sampler_linear.create(); m_sampler_linear.create();
m_sampler_linear_repeat.create(GL_LINEAR, GL_REPEAT); m_sampler_linear_repeat.create(GL_LINEAR, GL_REPEAT);
m_sampler_mipmap.create(); m_sampler_mipmap.create();
m_sampler_mipmap.set_filter(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR); m_sampler_mipmap.set_filter(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR);
m_brush_shape.create(); m_brush_shape.create();
App::I.async_end();
while (s_running) while (s_running)
{ {
auto node = s_queue.Get(); auto node = s_queue.Get();
@@ -530,43 +528,41 @@ void NodeStrokePreview::draw_stroke()
bool to_unload = (node->m_brush->m_tip_texture == nullptr); bool to_unload = (node->m_brush->m_tip_texture == nullptr);
node->m_brush->preload(); node->m_brush->preload();
node->async_start(); App::I.render_task([node, to_unload]
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); gl_state gl;
m_rtt_mixer.create((int)new_size.x, (int)new_size.y); gl.save();
m_tex.create((int)new_size.x, (int)new_size.y);
m_tex_dual.create((int)new_size.x, (int)new_size.y); auto new_size = node->m_preview_size;
m_tex_background.create((int)new_size.x, (int)new_size.y); 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::sleep_for(std::chrono::milliseconds(30));
std::this_thread::yield(); std::this_thread::yield();
} }
} }
App::I.async_start();
m_rtt.destroy(); m_rtt.destroy();
m_rtt_mixer.destroy(); m_rtt_mixer.destroy();
m_tex.destroy(); m_tex.destroy();
m_tex_dual.destroy(); m_tex_dual.destroy();
m_tex_background.destroy(); m_tex_background.destroy();
m_brush_shape.destroy(); m_brush_shape.destroy();
App::I.async_end();
}); });
} }
s_queue.mutex.unlock(); s_queue.mutex.unlock();

View File

@@ -2,6 +2,7 @@
#include "log.h" #include "log.h"
#include "rtt.h" #include "rtt.h"
#include "util.h" #include "util.h"
#include "app.h"
RTT::RTT() RTT::RTT()
{ {
@@ -38,19 +39,22 @@ void RTT::resize(int width, int height)
{ {
RTT new_rtt; RTT new_rtt;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldDFboID); App::I.render_task([&]
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldRFboID); {
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_DRAW_FRAMEBUFFER, new_rtt.fboID);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 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); 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_DRAW_FRAMEBUFFER, oldDFboID);
glBindFramebuffer(GL_READ_FRAMEBUFFER, oldRFboID); glBindFramebuffer(GL_READ_FRAMEBUFFER, oldRFboID);
destroy(); destroy();
});
oldRFboID = 0; oldRFboID = 0;
oldDFboID = 0; oldDFboID = 0;
@@ -64,22 +68,25 @@ void RTT::resize(int width, int height)
void RTT::destroy() void RTT::destroy()
{ {
if (rboID) App::I.render_task_async([rboID=rboID, texID=texID, fboID=fboID]
{ {
glDeleteRenderbuffers(1, &rboID); if (rboID)
} {
if (texID) glDeleteRenderbuffers(1, &rboID);
{ }
unbindTexture(); if (texID)
glDeleteTextures(1, &texID); {
//LOG("TEX rtt destroy %d", texID) //unbindTexture();
} glDeleteTextures(1, &texID);
if (fboID) //LOG("TEX rtt destroy %d", texID)
{ }
unbindFramebuffer(); if (fboID)
glDeleteFramebuffers(1, &fboID); {
//LOG("RTT DESTROY %d", fboID); //unbindFramebuffer();
} glDeleteFramebuffers(1, &fboID);
//LOG("RTT DESTROY %d", fboID);
}
});
texID = 0; texID = 0;
fboID = 0; fboID = 0;
rboID = 0; rboID = 0;
@@ -89,101 +96,109 @@ void RTT::destroy()
void RTT::copy(const RTT & source) void RTT::copy(const RTT & source)
{ {
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldDFboID); App::I.render_task([&]
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldRFboID); {
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldDFboID);
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldRFboID);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboID); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboID);
glBindFramebuffer(GL_READ_FRAMEBUFFER, source.fboID); glBindFramebuffer(GL_READ_FRAMEBUFFER, source.fboID);
glBlitFramebuffer(0, 0, source.w, source.h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_LINEAR); glBlitFramebuffer(0, 0, source.w, source.h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_LINEAR);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oldDFboID); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oldDFboID);
glBindFramebuffer(GL_READ_FRAMEBUFFER, oldRFboID); glBindFramebuffer(GL_READ_FRAMEBUFFER, oldRFboID);
});
} }
bool RTT::create(int width, int height, int tex/* = -1*/, GLint internal_format, bool depth_buffer /*= false*/) bool RTT::create(int width, int height, int tex/* = -1*/, GLint internal_format, bool depth_buffer /*= false*/)
{ {
// Destroy any previously created object GLenum status = 0;
destroy(); App::I.render_task([&]
w = width;
h = height;
int_fmt = internal_format;
if (tex == -1)
{ {
glGenTextures(1, &texID); // Destroy any previously created object
//LOG("TEX rtt create %d", texID); destroy();
}
else
{
texID = tex;
}
auto ifmt = GL_UNSIGNED_BYTE; w = width;
if (internal_format == GL_RGBA32F) ifmt = GL_FLOAT; h = height;
if (internal_format == GL_RGBA16F) ifmt = GL_HALF_FLOAT; int_fmt = internal_format;
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 (tex == -1)
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"; glGenTextures(1, &texID);
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; //LOG("TEX rtt create %d", texID);
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; }
case GL_FRAMEBUFFER_UNSUPPORTED: return "GL_FRAMEBUFFER_UNSUPPORTED"; else
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; {
default: return "UNKNOWN"; texID = tex;
} }
};
// Check FBO status auto ifmt = GL_UNSIGNED_BYTE;
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (internal_format == GL_RGBA32F) ifmt = GL_FLOAT;
if (status != GL_FRAMEBUFFER_COMPLETE) if (internal_format == GL_RGBA16F) ifmt = GL_HALF_FLOAT;
LOG("RTT::create failed because: %s", err2str(status));
// Switch back to window-system-provided framebuffer glBindTexture(GL_TEXTURE_2D, texID);
glBindFramebuffer(GL_FRAMEBUFFER, oldFboID); if (tex == -1)
oldRFboID = 0; glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, GL_RGBA, ifmt, 0);
oldDFboID = 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; return status == GL_FRAMEBUFFER_COMPLETE;
} }
void RTT::bindFramebuffer() void RTT::bindFramebuffer()
{ {
assert(App::I.is_render_thread());
#ifdef _DEBUG #ifdef _DEBUG
if (bound) if (bound)
{ {
@@ -193,15 +208,16 @@ void RTT::bindFramebuffer()
#endif #endif
} }
#endif // _DEBUG #endif // _DEBUG
bound = true;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldDFboID); glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldDFboID);
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldRFboID); glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldRFboID);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboID); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboID);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID); glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID);
bound = true;
} }
void RTT::unbindFramebuffer() void RTT::unbindFramebuffer()
{ {
assert(App::I.is_render_thread());
if (!bound) if (!bound)
return; return;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oldDFboID); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oldDFboID);
@@ -213,6 +229,7 @@ void RTT::unbindFramebuffer()
void RTT::clear(glm::vec4 color) void RTT::clear(glm::vec4 color)
{ {
assert(App::I.is_render_thread());
glClearColor(color.r, color.g, color.b, color.a); glClearColor(color.r, color.g, color.b, color.a);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
} }
@@ -240,11 +257,14 @@ uint8_t* RTT::readTextureData(uint8_t* buffer)
{ {
if (!buffer) if (!buffer)
buffer = createBuffer(); buffer = createBuffer();
GLint old; App::I.render_task([&]
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old); {
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID); GLint old;
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, buffer); glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old);
glBindFramebuffer(GL_READ_FRAMEBUFFER, old); glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID);
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
glBindFramebuffer(GL_READ_FRAMEBUFFER, old);
});
return buffer; return buffer;
} }
@@ -252,11 +272,14 @@ float* RTT::readTextureDataFloat(float* buffer)
{ {
if (!buffer) if (!buffer)
buffer = createBufferFloat(); buffer = createBufferFloat();
GLint old; App::I.render_task([&]
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old); {
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID); GLint old;
glReadPixels(0, 0, w, h, GL_RGBA, GL_FLOAT, buffer); glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old);
glBindFramebuffer(GL_READ_FRAMEBUFFER, old); glBindFramebuffer(GL_READ_FRAMEBUFFER, fboID);
glReadPixels(0, 0, w, h, GL_RGBA, GL_FLOAT, buffer);
glBindFramebuffer(GL_READ_FRAMEBUFFER, old);
});
return buffer; return buffer;
} }
@@ -272,11 +295,13 @@ float * RTT::createBufferFloat()
void RTT::bindTexture() void RTT::bindTexture()
{ {
assert(App::I.is_render_thread());
glBindTexture(GL_TEXTURE_2D, texID); glBindTexture(GL_TEXTURE_2D, texID);
} }
void RTT::unbindTexture() void RTT::unbindTexture()
{ {
assert(App::I.is_render_thread());
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
} }

View File

@@ -2,6 +2,7 @@
#include "log.h" #include "log.h"
#include "shader.h" #include "shader.h"
#include "asset.h" #include "asset.h"
#include "app.h"
std::map<kShader, Shader> ShaderManager::m_shaders; std::map<kShader, Shader> ShaderManager::m_shaders;
Shader* ShaderManager::m_current; Shader* ShaderManager::m_current;
@@ -153,122 +154,135 @@ bool Shader::reload()
bool Shader::create(const char* vertex, const char* fragment) bool Shader::create(const char* vertex, const char* fragment)
{ {
GLint status; bool ret = true;
static char infolog[4096]; App::I.render_task([&]
int infolen;
const GLchar* source;
auto vs = glCreateShader(GL_VERTEX_SHADER);
if (!vs)
{ {
return false; GLint status;
} static char infolog[4096];
source = vertex; int infolen;
glShaderSource(vs, 1, &source, nullptr); const GLchar* source;
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");
if (glGetAttribLocation(ps, "col") != -1) auto vs = glCreateShader(GL_VERTEX_SHADER);
glBindAttribLocation(ps, 3, "col"); if (!vs)
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++)
{ {
glGetActiveUniform(ps, (GLuint)i, bufSize, &length, &size, &type, name); ret = false;
m_umap[(kShaderUniform)const_hash(name)] = glGetUniformLocation(ps, name); return;
//printf("Uniform #%d Type: %u Name: %s Loc: %d\n", i, type, name, glGetUniformLocation(ps, name));
} }
} 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 ret;
return true;
} }
void Shader::destroy() void Shader::destroy()
{ {
if (prog) if (prog)
{ {
glUseProgram(0); App::I.render_task_async([prog=prog]
glDeleteProgram(prog); {
glUseProgram(0);
glDeleteProgram(prog);
});
prog = 0; prog = 0;
} }
m_umap.clear(); m_umap.clear();
@@ -276,16 +290,19 @@ void Shader::destroy()
void Shader::use() void Shader::use()
{ {
assert(App::I.is_render_thread());
glUseProgram(prog); glUseProgram(prog);
} }
void Shader::u_vec4(kShaderUniform id, const glm::vec4& v) 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) LOG("UNIFORM vec4 %d NOT FOUND in shader %d", (int)id, (int)name)
else glUniform4fv(m_umap[id], 1, glm::value_ptr(v)); else glUniform4fv(m_umap[id], 1, glm::value_ptr(v));
} }
void Shader::u_vec3(kShaderUniform id, const glm::vec3& v) void Shader::u_vec3(kShaderUniform id, const glm::vec3& v)
{ {
assert(App::I.is_render_thread());
if (m_umap.count(id) == 0) if (m_umap.count(id) == 0)
LOG("UNIFORM vec3 %d NOT FOUND in shader %d", (int)id, (int)name) LOG("UNIFORM vec3 %d NOT FOUND in shader %d", (int)id, (int)name)
else glUniform3fv(m_umap[id], 1, glm::value_ptr(v)); 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) 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) LOG("UNIFORM vec2 %d NOT FOUND in shader %d", (int)id, (int)name)
else glUniform2fv(m_umap[id], 1, glm::value_ptr(v)); else glUniform2fv(m_umap[id], 1, glm::value_ptr(v));
} }
void Shader::u_mat4(kShaderUniform id, const glm::mat4& m) 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) 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)); else glUniformMatrix4fv(m_umap[id], 1, GL_FALSE, glm::value_ptr(m));
} }
void Shader::u_int(kShaderUniform id, int i) void Shader::u_int(kShaderUniform id, int i)
{ {
assert(App::I.is_render_thread());
if (m_umap.count(id) == 0) if (m_umap.count(id) == 0)
LOG("UNIFORM int %d NOT FOUND in shader %d", (int)id, (int)name) LOG("UNIFORM int %d NOT FOUND in shader %d", (int)id, (int)name)
else else
@@ -313,12 +333,14 @@ void Shader::u_int(kShaderUniform id, int i)
} }
void Shader::u_float(kShaderUniform id, float f) 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) LOG("UNIFORM float %d NOT FOUND in shader %d", (int)id, (int)name)
else glUniform1f(m_umap[id], f); else glUniform1f(m_umap[id], f);
} }
GLint Shader::GetAttribLocation(const char* name) GLint Shader::GetAttribLocation(const char* name)
{ {
assert(App::I.is_render_thread());
return glGetAttribLocation(prog, name); return glGetAttribLocation(prog, name);
} }

View File

@@ -1,6 +1,7 @@
#include "pch.h" #include "pch.h"
#include "log.h" #include "log.h"
#include "shape.h" #include "shape.h"
#include "app.h"
bool Shape::create_buffers(GLushort * idx, GLvoid * vertices, int isize, int vsize) 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; use_idx = true;
destroy(); bool ret = false;
App::I.render_task([&]
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++)
{ {
glBindVertexArray(arrays[i]); destroy();
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1); glGenBuffers(2, buffers);
glEnableVertexAttribArray(2); if (!(buffers[0] && buffers[1]))
glEnableVertexAttribArray(3); {
ret = false;
return;
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, isize, idx, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)0); glBufferData(GL_ARRAY_BUFFER, vsize, vertices, GL_STATIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs)); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs2)); glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, nor));
} #if USE_VBO
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_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 #endif
return true; });
return ret;
} }
bool Shape::create_buffers(GLvoid* vertices, int vsize) bool Shape::create_buffers(GLvoid* vertices, int vsize)
{ {
use_idx = false; 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 #if USE_VBO
glGenVertexArrays(2, arrays); glGenVertexArrays(2, arrays);
if (!(arrays[0] && arrays[1])) if (!(arrays[0] && arrays[1]))
return false; {
for (int i = 0; i < 2; i++) ret = false;
{ return;
glBindVertexArray(arrays[i]); }
glEnableVertexAttribArray(0); for (int i = 0; i < 2; i++)
glEnableVertexAttribArray(1); {
glEnableVertexAttribArray(2); glBindVertexArray(arrays[i]);
glEnableVertexAttribArray(3); glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)0); glEnableVertexAttribArray(2);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs)); glEnableVertexAttribArray(3);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs2)); glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, nor)); 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));
glBindVertexArray(0); 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 #endif
return true; });
return ret;
} }
void Shape::draw_fill() const void Shape::draw_fill() const
{ {
@@ -103,31 +124,34 @@ void Shape::draw_fill() const
type = GL_POINTS; type = GL_POINTS;
if (count[0] == 2) if (count[0] == 2)
type = GL_LINES; type = GL_LINES;
#if USE_VBO App::I.render_task([=]
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]); #if USE_VBO
glDrawElements(type, count[0], GL_UNSIGNED_SHORT, ioff[0]); glBindVertexArray(arrays[0]);
} if (use_idx)
else glDrawElements(type, count[0], index_type, ioff[0]);
glDrawArrays(type, 0, count[0]); else
glDisableVertexAttribArray(0); glDrawArrays(type, 0, count[0]);
glDisableVertexAttribArray(1); glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); #else
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 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 #endif // USE_VBO
});
} }
void Shape::draw_stroke() const void Shape::draw_stroke() const
{ {
@@ -136,32 +160,61 @@ void Shape::draw_stroke() const
GLenum type = GL_LINES; GLenum type = GL_LINES;
if (count[1] == 1) if (count[1] == 1)
type = GL_POINTS; type = GL_POINTS;
#if USE_VBO App::I.render_task([=]
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)
{ {
#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_ELEMENT_ARRAY_BUFFER, buffers[1]);
glDrawElements(type, count[1], GL_UNSIGNED_SHORT, ioff[1]); glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
} glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)0);
else glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (GLvoid*)offsetof(vertex_t, uvs));
glDrawArrays(type, 0, count[1]); if (use_idx)
glDisableVertexAttribArray(0); {
glDisableVertexAttribArray(1); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
glBindBuffer(GL_ARRAY_BUFFER, 0); glDrawElements(type, count[1], GL_UNSIGNED_SHORT, ioff[1]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); }
else
glDrawArrays(type, 0, count[1]);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
#endif // USE_VBO #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) 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; vertices[i].pos.z = q;
} }
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); App::I.render_task([this]
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); {
static GLushort idx[6 + 8]{ glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
0, 1, 2, glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
0, 2, 3, static GLushort idx[6 + 8]{
0, 1, 0, 1, 2,
1, 2, 0, 2, 3,
2, 3, 0, 1,
3, 0, 1, 2,
}; 2, 3,
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); 3, 0,
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(idx), idx, 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, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
});
} }
void Circle::create_impl(float radius, int div, GLushort* idx, vertex_t* vertices) 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]) void LineSegment::update_vertices(const glm::vec4 data[2])
{ {
static vertex_t vertices[2]; App::I.render_task([&]
vertices[0] = { data[0], { 0, 0 } }; // A {
vertices[1] = { data[1], { 0, 1 } }; // B static vertex_t vertices[2];
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); vertices[0] = { data[0], { 0, 0 } }; // A
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); vertices[1] = { data[1], { 0, 1 } }; // B
glBindBuffer(GL_ARRAY_BUFFER, 0); 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) void DynamicShape::update_vertices(vertex_t* vertices, int vcount)
{ {
count[0] = vcount; App::I.render_task([&]
count[1] = vcount; {
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); count[0] = vcount;
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_t) * vcount, vertices, GL_STATIC_DRAW); count[1] = vcount;
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_t) * vcount, vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
});
} }

View File

@@ -25,22 +25,9 @@ public:
bool create_buffers(GLvoid* vertices, int vsize); bool create_buffers(GLvoid* vertices, int vsize);
void draw_fill() const; void draw_fill() const;
void draw_stroke() const; void draw_stroke() const;
void destroy() 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;
}
virtual bool create_attrib() { return true; }; virtual bool create_attrib() { return true; };
~Shape() ~Shape();
{
destroy();
}
protected: protected:
glm::vec2 quad_mid_point(glm::vec2 a, glm::vec2 b, glm::vec2 c, glm::vec2 d) glm::vec2 quad_mid_point(glm::vec2 a, glm::vec2 b, glm::vec2 c, glm::vec2 d)
{ {

View File

@@ -41,19 +41,22 @@ void TextureManager::invalidate()
bool Texture2D::create(int width, int height, GLint internal_format, GLint format, const uint8_t* data) bool Texture2D::create(int width, int height, GLint internal_format, GLint format, const uint8_t* data)
{ {
destroy(); App::I.render_task([=]
m_width = width; {
m_height = height; destroy();
m_format = format; m_width = width;
m_iformat = internal_format; m_height = height;
glGenTextures(1, &m_tex); m_format = format;
//LOG("TEX create %d", m_tex); m_iformat = internal_format;
bind(); glGenTextures(1, &m_tex);
auto ifmt = GL_UNSIGNED_BYTE; //LOG("TEX create %d", m_tex);
if (internal_format == GL_RGBA32F) ifmt = GL_FLOAT; bind();
if (internal_format == GL_RGBA16F) ifmt = GL_HALF_FLOAT; auto ifmt = GL_UNSIGNED_BYTE;
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, ifmt, data); if (internal_format == GL_RGBA32F) ifmt = GL_FLOAT;
unbind(); 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; return true;
} }
bool Texture2D::create(const Image& img) bool Texture2D::create(const Image& img)
@@ -65,10 +68,13 @@ bool Texture2D::create(const Image& img)
void Texture2D::create_mipmaps() void Texture2D::create_mipmaps()
{ {
bind(); App::I.render_task([this]
glGenerateMipmap(GL_TEXTURE_2D); {
unbind(); bind();
has_mips = true; 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*/) 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); 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) void Texture2D::update(const uint8_t* data)
{ {
bind(); App::I.render_task([this, data]
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_width, m_height, m_format, GL_UNSIGNED_BYTE, data); {
bind();
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_width, m_height, m_format, GL_UNSIGNED_BYTE, data);
});
} }
glm::vec2 Texture2D::size() const glm::vec2 Texture2D::size() const
@@ -113,57 +146,81 @@ Texture2D::~Texture2D()
{ {
if (auto_destroy) if (auto_destroy)
{ {
LOG("Texture2D auto destroy"); App::I.render_task_async([this]
App::I.async_start(); {
destroy(); LOG("Texture2D auto destroy");
App::I.async_end(); destroy();
});
} }
} }
bool Sampler::create(GLint filter /*= GL_LINEAR*/, GLint wrap /*= GL_CLAMP_TO_EDGE*/) 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 #if USE_SAMPLER
glGenSamplers(1, &id); glGenSamplers(1, &id);
#endif // USE_SAMPLER #endif // USE_SAMPLER
if (id == 0) if (id == 0)
return false; {
set(filter, wrap); ret = false;
return true; return;
}
set(filter, wrap);
ret = true;
});
return ret;
} }
void Sampler::set(GLint filter /*= GL_LINEAR*/, GLint wrap /*= GL_CLAMP_TO_EDGE*/) void Sampler::set(GLint filter /*= GL_LINEAR*/, GLint wrap /*= GL_CLAMP_TO_EDGE*/)
{ {
App::I.render_task([=]
{
#if USE_SAMPLER #if USE_SAMPLER
glSamplerParameteri(id, GL_TEXTURE_WRAP_S, wrap); glSamplerParameteri(id, GL_TEXTURE_WRAP_S, wrap);
glSamplerParameteri(id, GL_TEXTURE_WRAP_T, wrap); glSamplerParameteri(id, GL_TEXTURE_WRAP_T, wrap);
glSamplerParameteri(id, GL_TEXTURE_WRAP_R, wrap); glSamplerParameteri(id, GL_TEXTURE_WRAP_R, wrap);
glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, filter); glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, filter);
glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, filter); glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, filter);
#endif // USE_SAMPLER #endif // USE_SAMPLER
});
} }
void Sampler::set_filter(GLint filter_min, GLint filter_mag) void Sampler::set_filter(GLint filter_min, GLint filter_mag)
{ {
App::I.render_task([=]
{
#if USE_SAMPLER #if USE_SAMPLER
glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, filter_min); glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, filter_min);
glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, filter_mag); glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, filter_mag);
#endif // USE_SAMPLER #endif // USE_SAMPLER
});
} }
void Sampler::set_border(glm::vec4 rgba) void Sampler::set_border(glm::vec4 rgba)
{ {
App::I.render_task([this, rgba]
{
#if USE_SAMPLER && !defined(__IOS__) && !defined(__ANDROID__) #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 #endif // USE_SAMPLER
});
} }
void Sampler::bind(int unit) const void Sampler::bind(int unit) const
{ {
current_unit = unit; App::I.render_task([=]
{
current_unit = unit;
#if USE_SAMPLER #if USE_SAMPLER
glBindSampler(unit, id); glBindSampler(unit, id);
#endif // USE_SAMPLER #endif // USE_SAMPLER
});
} }
void Sampler::unbind() void Sampler::unbind()
{ {
App::I.render_task([=]
{
#if USE_SAMPLER #if USE_SAMPLER
glBindSampler(current_unit, 0); glBindSampler(current_unit, 0);
#endif // USE_SAMPLER #endif // USE_SAMPLER
});
} }

View File

@@ -16,9 +16,9 @@ public:
void assign(GLuint tex, int w = -1, int h = -1, GLuint internal_format = GL_RGBA8, GLuint format = GL_RGBA); 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(std::string filename);
bool load_file(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 destroy();
void bind() const { glBindTexture(GL_TEXTURE_2D, m_tex); } void bind() const;
void unbind() const { glBindTexture(GL_TEXTURE_2D, 0); } void unbind() const;
void update(const uint8_t* data); void update(const uint8_t* data);
bool ready() const { return m_tex != 0; } bool ready() const { return m_tex != 0; }
void create_mipmaps(); void create_mipmaps();