diff --git a/panda/src/display/displayRegion.cxx b/panda/src/display/displayRegion.cxx index e32b0f5a50..e1765861e1 100644 --- a/panda/src/display/displayRegion.cxx +++ b/panda/src/display/displayRegion.cxx @@ -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; } diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index 0b4e8448b1..dd637d7a9a 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -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); diff --git a/panda/src/display/graphicsEngine.h b/panda/src/display/graphicsEngine.h index 667490b3d3..3371f20b39 100644 --- a/panda/src/display/graphicsEngine.h +++ b/panda/src/display/graphicsEngine.h @@ -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; };