Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pull] master from panda3d:master #171

Merged
merged 8 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion panda/src/display/displayRegion.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,9 @@ get_screenshot() {
if (gsg->get_threading_model().get_draw_stage() != current_thread->get_pipeline_stage()) {
// Ask the engine to do on the draw thread.
GraphicsEngine *engine = window->get_engine();
return engine->do_get_screenshot(this, gsg);
return engine->run_on_draw_thread([this] {
return get_screenshot();
});
}

// We are on the draw thread.
Expand Down
50 changes: 50 additions & 0 deletions panda/src/display/graphicsEngine.I
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,53 @@ INLINE void GraphicsEngine::
dispatch_compute(const LVecBase3i &work_groups, const ShaderAttrib *sattr, GraphicsStateGuardian *gsg) {
dispatch_compute(work_groups, RenderState::make(sattr), gsg);
}

#ifndef CPPPARSER
/**
* Waits for the draw thread to become idle, then runs the given function on it.
*/
template<class Callable>
INLINE auto GraphicsEngine::
run_on_draw_thread(Callable &&callable) -> decltype(callable()) {
ReMutexHolder holder(_lock);
std::string draw_name = _threading_model.get_draw_name();
if (draw_name.empty()) {
return std::move(callable)();
} else {
WindowRenderer *wr = get_window_renderer(draw_name, 0);
RenderThread *thread = (RenderThread *)wr;
return thread->run_on_thread(std::move(callable));
}
}

/**
* Waits for this thread to become idle, then runs the given function on it.
*/
template<class Callable>
INLINE auto GraphicsEngine::RenderThread::
run_on_thread(Callable &&callable) ->
typename std::enable_if<!std::is_void<decltype(callable())>::value, decltype(callable())>::type {

using ReturnType = decltype(callable());
alignas(ReturnType) unsigned char storage[sizeof(ReturnType)];

run_on_thread([] (RenderThread *data) {
new (data->_return_data) ReturnType(std::move(*(Callable *)data->_callback_data)());
}, &callable, storage);

return *(ReturnType *)storage;
}

/**
* Waits for this thread to become idle, then runs the given function on it.
*/
template<class Callable>
INLINE auto GraphicsEngine::RenderThread::
run_on_thread(Callable &&callable) ->
typename std::enable_if<std::is_void<decltype(callable())>::value, decltype(callable())>::type {

run_on_thread([] (RenderThread *data) {
std::move(*(Callable *)data->_callback_data)();
}, &callable, nullptr);
}
#endif // CPPPARSER
232 changes: 45 additions & 187 deletions panda/src/display/graphicsEngine.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1134,47 +1134,9 @@ flip_frame() {
*/
bool GraphicsEngine::
extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg) {
ReMutexHolder holder(_lock);

string draw_name = gsg->get_threading_model().get_draw_name();
if (draw_name.empty()) {
// A single-threaded environment. No problem.
return run_on_draw_thread([=] () {
return gsg->extract_texture_data(tex);

} else {
// A multi-threaded environment. We have to wait until the draw thread
// has finished its current task.
WindowRenderer *wr = get_window_renderer(draw_name, 0);
RenderThread *thread = (RenderThread *)wr;
MutexHolder cv_holder(thread->_cv_mutex);

while (thread->_thread_state != TS_wait) {
thread->_cv_done.wait();
}

// Temporarily set this so that it accesses data from the current thread.
int pipeline_stage = Thread::get_current_pipeline_stage();
int draw_pipeline_stage = thread->get_pipeline_stage();
thread->set_pipeline_stage(pipeline_stage);

// Now that the draw thread is idle, signal it to do the extraction task.
thread->_gsg = gsg;
thread->_texture = tex;
thread->_thread_state = TS_do_extract_texture_data;
thread->_cv_mutex.release();
thread->_cv_start.notify();
thread->_cv_mutex.acquire();

// Wait for it to finish the extraction.
while (thread->_thread_state != TS_wait) {
thread->_cv_done.wait();
}

thread->set_pipeline_stage(draw_pipeline_stage);
thread->_gsg = nullptr;
thread->_texture = nullptr;
return thread->_result;
}
});
}

/**
Expand All @@ -1189,56 +1151,13 @@ extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg) {
*/
vector_uchar GraphicsEngine::
extract_shader_buffer_data(ShaderBuffer *buffer, GraphicsStateGuardian *gsg) {
ReMutexHolder holder(_lock);

string draw_name = gsg->get_threading_model().get_draw_name();
if (draw_name.empty()) {
// A single-threaded environment. No problem.
return run_on_draw_thread([=] () {
vector_uchar data;
if (!gsg->extract_shader_buffer_data(buffer, data)) {
data.clear();
}
return data;
}

// A multi-threaded environment. We have to wait until the draw thread
// has finished its current task.
WindowRenderer *wr = get_window_renderer(draw_name, 0);
RenderThread *thread = (RenderThread *)wr;
MutexHolder cv_holder(thread->_cv_mutex);

while (thread->_thread_state != TS_wait) {
thread->_cv_done.wait();
}

// Temporarily set this so that it accesses data from the current thread.
int pipeline_stage = Thread::get_current_pipeline_stage();
int draw_pipeline_stage = thread->get_pipeline_stage();
thread->set_pipeline_stage(pipeline_stage);

// Now that the draw thread is idle, signal it to do the extraction task.
vector_uchar data;
thread->_gsg = gsg;
thread->_buffer = buffer;
thread->_buffer_result = &data;
thread->_thread_state = TS_do_extract_shader_buffer_data;
thread->_cv_mutex.release();
thread->_cv_start.notify();
thread->_cv_mutex.acquire();

// Wait for it to finish the extraction.
while (thread->_thread_state != TS_wait) {
thread->_cv_done.wait();
}

thread->set_pipeline_stage(draw_pipeline_stage);
thread->_gsg = nullptr;
thread->_buffer = nullptr;
thread->_buffer_result = nullptr;
if (!thread->_result) {
data.clear();
}
return data;
});
}

/**
Expand All @@ -1263,50 +1182,12 @@ dispatch_compute(const LVecBase3i &work_groups, const RenderState *state, Graphi
nassertv(shader != nullptr);
nassertv(gsg != nullptr);

ReMutexHolder holder(_lock);

string draw_name = gsg->get_threading_model().get_draw_name();
if (draw_name.empty()) {
// A single-threaded environment. No problem.
run_on_draw_thread([=] () {
gsg->push_group_marker(std::string("Compute ") + shader->get_filename(Shader::ST_compute).get_basename());
gsg->set_state_and_transform(state, TransformState::make_identity());
gsg->dispatch_compute(work_groups[0], work_groups[1], work_groups[2]);
gsg->pop_group_marker();

} else {
// A multi-threaded environment. We have to wait until the draw thread
// has finished its current task.
WindowRenderer *wr = get_window_renderer(draw_name, 0);
RenderThread *thread = (RenderThread *)wr;
MutexHolder cv_holder(thread->_cv_mutex);

while (thread->_thread_state != TS_wait) {
thread->_cv_done.wait();
}

// Temporarily set this so that it accesses data from the current thread.
int pipeline_stage = Thread::get_current_pipeline_stage();
int draw_pipeline_stage = thread->get_pipeline_stage();
thread->set_pipeline_stage(pipeline_stage);

// Now that the draw thread is idle, signal it to do the compute task.
thread->_gsg = gsg;
thread->_state = state;
thread->_work_groups = work_groups;
thread->_thread_state = TS_do_compute;
thread->_cv_mutex.release();
thread->_cv_start.notify();
thread->_cv_mutex.acquire();

// Wait for it to finish the compute task.
while (thread->_thread_state != TS_wait) {
thread->_cv_done.wait();
}

thread->set_pipeline_stage(draw_pipeline_stage);
thread->_gsg = nullptr;
thread->_state = nullptr;
}
});
}

/**
Expand Down Expand Up @@ -1342,43 +1223,6 @@ texture_uploaded(Texture *tex) {
// Usually only called by DisplayRegion::do_cull.
}

/**
* Called by DisplayRegion::do_get_screenshot
*/
PT(Texture) GraphicsEngine::
do_get_screenshot(DisplayRegion *region, GraphicsStateGuardian *gsg) {
// A multi-threaded environment. We have to wait until the draw thread
// has finished its current task.

ReMutexHolder holder(_lock);

const std::string &draw_name = gsg->get_threading_model().get_draw_name();
WindowRenderer *wr = get_window_renderer(draw_name, 0);
RenderThread *thread = (RenderThread *)wr;
MutexHolder cv_holder(thread->_cv_mutex);

while (thread->_thread_state != TS_wait) {
thread->_cv_done.wait();
}

// Now that the draw thread is idle, signal it to do the extraction task.
thread->_region = region;
thread->_thread_state = TS_do_screenshot;
thread->_cv_mutex.release();
thread->_cv_start.notify();
thread->_cv_mutex.acquire();

// Wait for it to finish the extraction.
while (thread->_thread_state != TS_wait) {
thread->_cv_done.wait();
}

PT(Texture) tex = std::move(thread->_texture);
thread->_region = nullptr;
thread->_texture = nullptr;
return tex;
}

/**
* Fires off a cull traversal using the indicated camera.
*/
Expand Down Expand Up @@ -2867,31 +2711,9 @@ thread_main() {
do_pending(_engine, current_thread);
break;

case TS_do_compute:
nassertd(_gsg != nullptr && _state != nullptr) break;
{
const ShaderAttrib *sattr;
_state->get_attrib(sattr);
_gsg->push_group_marker(std::string("Compute ") + sattr->get_shader()->get_filename(Shader::ST_compute).get_basename());
_gsg->set_state_and_transform(_state, TransformState::make_identity());
_gsg->dispatch_compute(_work_groups[0], _work_groups[1], _work_groups[2]);
_gsg->pop_group_marker();
}
break;

case TS_do_extract_texture_data:
nassertd(_gsg != nullptr && _texture != nullptr) break;
_result = _gsg->extract_texture_data(_texture);
break;

case TS_do_extract_shader_buffer_data:
nassertd(_gsg != nullptr && _texture != nullptr) break;
_result = _gsg->extract_shader_buffer_data(_buffer, *_buffer_result);
break;

case TS_do_screenshot:
nassertd(_region != nullptr) break;
_texture = _region->get_screenshot();
case TS_callback:
nassertd(_callback != nullptr) break;
_callback(this);
break;

case TS_terminate:
Expand All @@ -2916,3 +2738,39 @@ thread_main() {
}
}
}

/**
* Waits for this thread to become idle, then runs the given function on it.
*/
void GraphicsEngine::RenderThread::
run_on_thread(Callback *callback, void *callback_data, void *return_data) {
MutexHolder cv_holder(_cv_mutex);

while (_thread_state != TS_wait) {
_cv_done.wait();
}

// Temporarily set this so that it accesses data from the current thread.
int pipeline_stage = Thread::get_current_pipeline_stage();
int thread_pipeline_stage = get_pipeline_stage();
set_pipeline_stage(pipeline_stage);

// Now that the draw thread is idle, signal it to run the callback.
_callback = callback;
_callback_data = callback_data;
_return_data = return_data;
_thread_state = TS_callback;
_cv_mutex.release();
_cv_start.notify();
_cv_mutex.acquire();

// Wait for it to finish the job.
while (_thread_state != TS_wait) {
_cv_done.wait();
}

set_pipeline_stage(thread_pipeline_stage);
_callback = nullptr;
_callback_data = nullptr;
_return_data = nullptr;
}
Loading
Loading