mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-01 17:35:34 -04:00
display: fix ability to make screenshots in multithreaded pipeline
Problem is that WGL is strict about binding context in different thread while it is still bound in another thread. Either way we need to make sure the draw thread is not rendering, so if you call get_screenshot() from a thread other than the draw thread, it uses the GraphicsEngine to wait until the draw thread is idle and then asks it to do the get_screenshot(). Fixes #360
This commit is contained in:
parent
b85fead09d
commit
d4d582484f
@ -483,6 +483,14 @@ get_screenshot() {
|
||||
GraphicsStateGuardian *gsg = window->get_gsg();
|
||||
nassertr(gsg != nullptr, nullptr);
|
||||
|
||||
// Are we on the draw thread?
|
||||
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);
|
||||
}
|
||||
|
||||
// We are on the draw thread.
|
||||
if (!window->begin_frame(GraphicsOutput::FM_refresh, current_thread)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1249,6 +1249,43 @@ 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_start.notify();
|
||||
thread->_cv_mutex.release();
|
||||
thread->_cv_mutex.acquire();
|
||||
|
||||
//XXX is this necessary, or is acquiring the mutex enough?
|
||||
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.
|
||||
*/
|
||||
@ -2633,6 +2670,11 @@ thread_main() {
|
||||
_result = _gsg->extract_texture_data(_texture);
|
||||
break;
|
||||
|
||||
case TS_do_screenshot:
|
||||
nassertd(_region != nullptr) break;
|
||||
_texture = _region->get_screenshot();
|
||||
break;
|
||||
|
||||
case TS_terminate:
|
||||
do_pending(_engine, current_thread);
|
||||
do_close(_engine, current_thread);
|
||||
|
@ -125,11 +125,13 @@ public:
|
||||
TS_do_windows,
|
||||
TS_do_compute,
|
||||
TS_do_extract,
|
||||
TS_do_screenshot,
|
||||
TS_terminate,
|
||||
TS_done
|
||||
};
|
||||
|
||||
void texture_uploaded(Texture *tex);
|
||||
PT(Texture) do_get_screenshot(DisplayRegion *region, GraphicsStateGuardian *gsg);
|
||||
|
||||
public:
|
||||
static void do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
|
||||
@ -304,8 +306,9 @@ private:
|
||||
|
||||
// These are stored for extract_texture_data and dispatch_compute.
|
||||
GraphicsStateGuardian *_gsg;
|
||||
Texture *_texture;
|
||||
PT(Texture) _texture;
|
||||
const RenderState *_state;
|
||||
DisplayRegion *_region;
|
||||
LVecBase3i _work_groups;
|
||||
bool _result;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user