display: Add support for asynchronous screenshot download

This commit is contained in:
rdb 2022-12-26 19:25:53 +01:00
parent d5fed54a0c
commit cfd18bb16f
20 changed files with 702 additions and 28 deletions

View File

@ -2713,7 +2713,7 @@ class ShowBase(DirectObject.DirectObject):
def screenshot(self, namePrefix = 'screenshot', def screenshot(self, namePrefix = 'screenshot',
defaultFilename = 1, source = None, defaultFilename = 1, source = None,
imageComment=""): imageComment="", blocking=True):
""" Captures a screenshot from the main window or from the """ Captures a screenshot from the main window or from the
specified window or Texture and writes it to a filename in the specified window or Texture and writes it to a filename in the
current directory (or to a specified directory). current directory (or to a specified directory).
@ -2735,6 +2735,13 @@ class ShowBase(DirectObject.DirectObject):
generated by makeCubeMap(), namePrefix should contain the hash generated by makeCubeMap(), namePrefix should contain the hash
mark ('#') character. mark ('#') character.
Normally, this call will block until the screenshot is fully
written. To write the screenshot in a background thread
instead, pass blocking = False. In this case, the return value
is a future that can be awaited.
A "screenshot" event will be sent once the screenshot is saved.
:returns: The filename if successful, or None if there is a problem. :returns: The filename if successful, or None if there is a problem.
""" """
@ -2751,8 +2758,12 @@ class ShowBase(DirectObject.DirectObject):
saved = source.write(filename, 0, 0, 1, 0) saved = source.write(filename, 0, 0, 1, 0)
else: else:
saved = source.write(filename) saved = source.write(filename)
else: elif blocking:
saved = source.saveScreenshot(filename, imageComment) saved = source.saveScreenshot(filename, imageComment)
else:
request = source.saveAsyncScreenshot(filename, imageComment)
request.addDoneCallback(lambda fut, filename=filename: messenger.send('screenshot', [filename]))
return request
if saved: if saved:
# Announce to anybody that a screenshot has been taken # Announce to anybody that a screenshot has been taken

View File

@ -27,6 +27,7 @@ set(P3DISPLAY_HEADERS
windowHandle.I windowHandle.h windowHandle.I windowHandle.h
windowProperties.I windowProperties.h windowProperties.I windowProperties.h
renderBuffer.h renderBuffer.h
screenshotRequest.I screenshotRequest.h
stereoDisplayRegion.I stereoDisplayRegion.h stereoDisplayRegion.I stereoDisplayRegion.h
displaySearchParameters.h displaySearchParameters.h
displayInformation.h displayInformation.h
@ -61,6 +62,7 @@ set(P3DISPLAY_SOURCES
parasiteBuffer.cxx parasiteBuffer.cxx
windowHandle.cxx windowHandle.cxx
windowProperties.cxx windowProperties.cxx
screenshotRequest.cxx
stereoDisplayRegion.cxx stereoDisplayRegion.cxx
subprocessWindow.cxx subprocessWindow.cxx
touchInfo.cxx touchInfo.cxx

View File

@ -29,6 +29,7 @@
#include "nativeWindowHandle.h" #include "nativeWindowHandle.h"
#include "parasiteBuffer.h" #include "parasiteBuffer.h"
#include "pandaSystem.h" #include "pandaSystem.h"
#include "screenshotRequest.h"
#include "stereoDisplayRegion.h" #include "stereoDisplayRegion.h"
#include "subprocessWindow.h" #include "subprocessWindow.h"
#include "windowHandle.h" #include "windowHandle.h"
@ -534,6 +535,7 @@ init_libdisplay() {
MouseAndKeyboard::init_type(); MouseAndKeyboard::init_type();
NativeWindowHandle::init_type(); NativeWindowHandle::init_type();
ParasiteBuffer::init_type(); ParasiteBuffer::init_type();
ScreenshotRequest::init_type();
StandardMunger::init_type(); StandardMunger::init_type();
StereoDisplayRegion::init_type(); StereoDisplayRegion::init_type();
#ifdef SUPPORT_SUBPROCESS_WINDOW #ifdef SUPPORT_SUBPROCESS_WINDOW

View File

@ -1419,6 +1419,8 @@ cull_and_draw_together(GraphicsEngine::Windows wlist,
} }
if (win->begin_frame(GraphicsOutput::FM_render, current_thread)) { if (win->begin_frame(GraphicsOutput::FM_render, current_thread)) {
win->copy_async_screenshot();
if (win->is_any_clear_active()) { if (win->is_any_clear_active()) {
GraphicsStateGuardian *gsg = win->get_gsg(); GraphicsStateGuardian *gsg = win->get_gsg();
PStatGPUTimer timer(gsg, win->get_clear_window_pcollector(), current_thread); PStatGPUTimer timer(gsg, win->get_clear_window_pcollector(), current_thread);
@ -1720,6 +1722,9 @@ draw_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
// a current context for PStatGPUTimer to work. // a current context for PStatGPUTimer to work.
{ {
PStatGPUTimer timer(gsg, win->get_draw_window_pcollector(), current_thread); PStatGPUTimer timer(gsg, win->get_draw_window_pcollector(), current_thread);
win->copy_async_screenshot();
if (win->is_any_clear_active()) { if (win->is_any_clear_active()) {
PStatGPUTimer timer(gsg, win->get_clear_window_pcollector(), current_thread); PStatGPUTimer timer(gsg, win->get_clear_window_pcollector(), current_thread);
win->get_gsg()->push_group_marker("Clear"); win->get_gsg()->push_group_marker("Clear");

View File

@ -976,6 +976,43 @@ make_cube_map(const string &name, int size, NodePath &camera_rig,
return buffer; return buffer;
} }
/**
* Like save_screenshot, but performs both the texture transfer and the saving
* to disk in the background. Returns a future that can be awaited.
*
* This captures the frame that was last submitted by the App stage to the
* render_frame() call. This may not be the latest frame shown on the screen
* if the multi-threaded pipeline is used, in which case the request may take
* several frames extra to complete.
*/
PT(ScreenshotRequest) GraphicsOutput::
save_async_screenshot(const Filename &filename, const std::string &image_comment) {
PT(ScreenshotRequest) request = get_async_screenshot();
request->add_output_file(filename, image_comment);
return request;
}
/**
* Used to obtain a new Texture object containing the previously rendered frame.
* Unlike get_screenshot, this works asynchronously, meaning that the contents
* are transferred in the background. Returns a future that can be awaited.
*
* This captures the frame that was last submitted by the App stage to the
* render_frame() call. This may not be the latest frame shown on the screen
* if the multi-threaded pipeline is used, in which case the request may take
* several frames extra to complete.
*/
PT(ScreenshotRequest) GraphicsOutput::
get_async_screenshot() {
Thread *current_thread = Thread::get_current_thread();
CDWriter cdata(_cycler, current_thread);
if (cdata->_screenshot_request == nullptr) {
PT(Texture) texture = new Texture("screenshot of " + get_name());
cdata->_screenshot_request = new ScreenshotRequest(texture);
}
return cdata->_screenshot_request;
}
/** /**
* Returns a PandaNode containing a square polygon. The dimensions are * Returns a PandaNode containing a square polygon. The dimensions are
* (-1,0,-1) to (1,0,1). The texture coordinates are such that the texture of * (-1,0,-1) to (1,0,1). The texture coordinates are such that the texture of
@ -1468,6 +1505,56 @@ copy_to_textures() {
return okflag; return okflag;
} }
/**
* Do the necessary copies for the get_async_screenshot request.
*/
void GraphicsOutput::
copy_async_screenshot() {
Thread *current_thread = Thread::get_current_thread();
PT(ScreenshotRequest) request;
{
CDWriter cdata(_cycler, current_thread);
if (cdata->_screenshot_request == nullptr) {
return;
}
request = std::move(cdata->_screenshot_request);
cdata->_screenshot_request.clear();
}
// Make sure it is cleared from upstream stages as well.
OPEN_ITERATE_UPSTREAM_ONLY(_cycler, current_thread) {
CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
if (cdata->_screenshot_request == request) {
cdata->_screenshot_request.clear();
}
}
CLOSE_ITERATE_UPSTREAM_ONLY(_cycler);
PStatTimer timer(_copy_texture_pcollector);
RenderBuffer buffer = _gsg->get_render_buffer(get_draw_buffer_type(),
get_fb_properties());
DisplayRegion *dr = _overlay_display_region;
Texture *texture = request->get_result();
if (_fb_properties.is_stereo()) {
// We've got two texture views to copy.
texture->set_num_views(2);
RenderBuffer left(_gsg, buffer._buffer_type & ~RenderBuffer::T_right);
RenderBuffer right(_gsg, buffer._buffer_type & ~RenderBuffer::T_left);
_gsg->framebuffer_copy_to_ram(texture, 0, _target_tex_page,
dr, left, request);
_gsg->framebuffer_copy_to_ram(texture, 1, _target_tex_page,
dr, right, request);
} else {
_gsg->framebuffer_copy_to_ram(texture, 0, _target_tex_page,
dr, buffer, request);
}
}
/** /**
* Generates a GeomVertexData for a texture card. * Generates a GeomVertexData for a texture card.
*/ */
@ -1653,7 +1740,8 @@ CData(const GraphicsOutput::CData &copy) :
_active(copy._active), _active(copy._active),
_one_shot_frame(copy._one_shot_frame), _one_shot_frame(copy._one_shot_frame),
_active_display_regions(copy._active_display_regions), _active_display_regions(copy._active_display_regions),
_active_display_regions_stale(copy._active_display_regions_stale) _active_display_regions_stale(copy._active_display_regions_stale),
_screenshot_request(copy._screenshot_request)
{ {
} }

View File

@ -41,6 +41,7 @@
#include "pipelineCycler.h" #include "pipelineCycler.h"
#include "updateSeq.h" #include "updateSeq.h"
#include "asyncFuture.h" #include "asyncFuture.h"
#include "screenshotRequest.h"
class PNMImage; class PNMImage;
class GraphicsEngine; class GraphicsEngine;
@ -239,6 +240,9 @@ PUBLISHED:
const Filename &filename, const std::string &image_comment = ""); const Filename &filename, const std::string &image_comment = "");
INLINE bool get_screenshot(PNMImage &image); INLINE bool get_screenshot(PNMImage &image);
INLINE PT(Texture) get_screenshot(); INLINE PT(Texture) get_screenshot();
PT(ScreenshotRequest) save_async_screenshot(const Filename &filename,
const std::string &image_comment = "");
PT(ScreenshotRequest) get_async_screenshot();
NodePath get_texture_card(); NodePath get_texture_card();
@ -298,6 +302,7 @@ protected:
void prepare_for_deletion(); void prepare_for_deletion();
void promote_to_copy_texture(); void promote_to_copy_texture();
bool copy_to_textures(); bool copy_to_textures();
void copy_async_screenshot();
INLINE void begin_frame_spam(FrameMode mode); INLINE void begin_frame_spam(FrameMode mode);
INLINE void end_frame_spam(FrameMode mode); INLINE void end_frame_spam(FrameMode mode);
@ -392,6 +397,8 @@ protected:
int _one_shot_frame; int _one_shot_frame;
ActiveDisplayRegions _active_display_regions; ActiveDisplayRegions _active_display_regions;
bool _active_display_regions_stale; bool _active_display_regions_stale;
PT(ScreenshotRequest) _screenshot_request;
}; };
PipelineCycler<CData> _cycler; PipelineCycler<CData> _cycler;
typedef CycleDataLockedReader<CData> CDLockedReader; typedef CycleDataLockedReader<CData> CDLockedReader;

View File

@ -3037,11 +3037,15 @@ framebuffer_copy_to_texture(Texture *, int, int, const DisplayRegion *,
* into system memory, not texture memory. Returns true on success, false on * into system memory, not texture memory. Returns true on success, false on
* failure. * failure.
* *
* If a future is given, the operation may be scheduled to occur in the
* background, in which case the texture will be passed as the result of the
* future when the operation is complete.
*
* This completely redefines the ram image of the indicated texture. * This completely redefines the ram image of the indicated texture.
*/ */
bool GraphicsStateGuardian:: bool GraphicsStateGuardian::
framebuffer_copy_to_ram(Texture *, int, int, const DisplayRegion *, framebuffer_copy_to_ram(Texture *, int, int, const DisplayRegion *,
const RenderBuffer &) { const RenderBuffer &, ScreenshotRequest *) {
return false; return false;
} }

View File

@ -426,7 +426,8 @@ public:
virtual bool framebuffer_copy_to_texture virtual bool framebuffer_copy_to_texture
(Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb); (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb);
virtual bool framebuffer_copy_to_ram virtual bool framebuffer_copy_to_ram
(Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb); (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb,
ScreenshotRequest *request = nullptr);
virtual void bind_light(PointLight *light_obj, const NodePath &light, virtual void bind_light(PointLight *light_obj, const NodePath &light,
int light_id); int light_id);

View File

@ -9,6 +9,7 @@
#include "parasiteBuffer.cxx" #include "parasiteBuffer.cxx"
#include "standardMunger.cxx" #include "standardMunger.cxx"
#include "touchInfo.cxx" #include "touchInfo.cxx"
#include "screenshotRequest.cxx"
#include "stereoDisplayRegion.cxx" #include "stereoDisplayRegion.cxx"
#include "subprocessWindow.cxx" #include "subprocessWindow.cxx"
#ifdef IS_OSX #ifdef IS_OSX

View File

@ -0,0 +1,39 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file screenshotRequest.I
* @author rdb
* @date 2022-12-26
*/
/**
*
*/
INLINE ScreenshotRequest::
ScreenshotRequest(Texture *tex) :
_frame_number(ClockObject::get_global_clock()->get_frame_count()) {
_result = tex;
_result_ref = tex;
}
/**
* Returns the frame number in which the request originated.
*/
INLINE int ScreenshotRequest::
get_frame_number() const {
return _frame_number;
}
/**
* Returns the resulting texture. Can always be called.
*/
INLINE Texture *ScreenshotRequest::
get_result() const {
return (Texture *)_result;
}

View File

@ -0,0 +1,104 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file screenshotRequest.cxx
* @author rdb
* @date 2022-12-26
*/
#include "screenshotRequest.h"
#include "lightMutexHolder.h"
#include "pnmImage.h"
#include "texture.h"
TypeHandle ScreenshotRequest::_type_handle;
/**
*
*/
void ScreenshotRequest::
set_view_data(int view, const void *ptr) {
const int z = 0;
Texture *tex = get_result();
PTA_uchar new_image = tex->modify_ram_image();
unsigned char *image_ptr = new_image.p();
size_t image_size = tex->get_ram_image_size();
if (z >= 0 || view > 0) {
image_size = tex->get_expected_ram_page_size();
if (z >= 0) {
image_ptr += z * image_size;
}
if (view > 0) {
image_ptr += (view * tex->get_z_size()) * image_size;
nassertd(view < tex->get_num_views()) {
if (set_future_state(FS_cancelled)) {
notify_done(false);
}
return;
}
}
}
memcpy(image_ptr, ptr, image_size);
}
/**
*
*/
void ScreenshotRequest::
finish() {
Texture *tex = get_result();
++_got_num_views;
if (_got_num_views < tex->get_num_views()) {
return;
}
{
LightMutexHolder holder(_lock);
if (!_output_files.empty()) {
PNMImage image;
tex->store(image);
for (const auto &item : _output_files) {
image.set_comment(item.second);
image.write(item.first);
}
}
AsyncFuture::set_result(tex);
_output_files.clear();
if (!set_future_state(FS_finished)) {
return;
}
}
notify_done(true);
}
/**
* Adds a filename to write the screenshot to when it is available. If the
* request is already done, performs the write synchronously.
*/
void ScreenshotRequest::
add_output_file(const Filename &filename, const std::string &image_comment) {
if (!done()) {
LightMutexHolder holder(_lock);
if (!done()) {
_output_files[filename] = image_comment;
return;
}
}
// Was already done, write it right away.
Texture *tex = get_result();
PNMImage image;
tex->store(image);
image.set_comment(image_comment);
image.write(filename);
}

View File

@ -0,0 +1,71 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file screenshotRequest.h
* @author rdb
* @date 2022-12-26
*/
#ifndef SCREENSHOTREQUEST_H
#define SCREENSHOTREQUEST_H
#include "pandabase.h"
#include "asyncFuture.h"
#include "filename.h"
#include "lightMutex.h"
#include "pmap.h"
/**
* A class representing an asynchronous request to save a screenshot.
*/
class EXPCL_PANDA_PGRAPH ScreenshotRequest : public AsyncFuture {
public:
INLINE ScreenshotRequest(Texture *tex);
INLINE int get_frame_number() const;
INLINE Texture *get_result() const;
void set_view_data(int view, const void *ptr);
void finish();
PUBLISHED:
void add_output_file(const Filename &filename,
const std::string &image_comment = "");
private:
// It's possible to call save_screenshot multiple times in the same frame, so
// rather than have to store a vector of request objects, we just allow
// storing multiple filenames to handle this corner case.
LightMutex _lock;
pmap<Filename, std::string> _output_files;
int _got_num_views = 0;
int _frame_number = 0;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
AsyncFuture::init_type();
register_type(_type_handle, "ScreenshotRequest",
AsyncFuture::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#include "screenshotRequest.I"
#endif

View File

@ -1988,8 +1988,17 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z,
*/ */
bool DXGraphicsStateGuardian9:: bool DXGraphicsStateGuardian9::
framebuffer_copy_to_ram(Texture *tex, int view, int z, framebuffer_copy_to_ram(Texture *tex, int view, int z,
const DisplayRegion *dr, const RenderBuffer &rb) { const DisplayRegion *dr, const RenderBuffer &rb,
return do_framebuffer_copy_to_ram(tex, view, z, dr, rb, false); ScreenshotRequest *request) {
bool success = do_framebuffer_copy_to_ram(tex, view, z, dr, rb, false);
if (request != nullptr) {
if (success) {
request->finish();
} else {
request->cancel();
}
}
return success;
} }
/** /**

View File

@ -127,7 +127,8 @@ public:
const RenderBuffer &rb); const RenderBuffer &rb);
virtual bool framebuffer_copy_to_ram(Texture *tex, int view, int z, virtual bool framebuffer_copy_to_ram(Texture *tex, int view, int z,
const DisplayRegion *dr, const DisplayRegion *dr,
const RenderBuffer &rb); const RenderBuffer &rb,
ScreenshotRequest *request);
bool do_framebuffer_copy_to_ram(Texture *tex, int view, int z, bool do_framebuffer_copy_to_ram(Texture *tex, int view, int z,
const DisplayRegion *dr, const DisplayRegion *dr,
const RenderBuffer &rb, const RenderBuffer &rb,

View File

@ -177,6 +177,7 @@ typedef char GLchar;
#define GL_READ_ONLY 0x88B8 #define GL_READ_ONLY 0x88B8
#define GL_WRITE_ONLY 0x88B9 #define GL_WRITE_ONLY 0x88B9
#define GL_READ_WRITE 0x88BA #define GL_READ_WRITE 0x88BA
#define GL_PIXEL_PACK_BUFFER 0x88EB
#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF #define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF
#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 #define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35
#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 #define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36
@ -253,6 +254,12 @@ typedef char GLchar;
#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 #define GL_UNSIGNED_INT_IMAGE_3D 0x9064
#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 #define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066
#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 #define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069
#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117
#define GL_UNSIGNALED 0x9118
#define GL_SIGNALED 0x9119
#define GL_ALREADY_SIGNALED 0x911A
#define GL_TIMEOUT_EXPIRED 0x911B
#define GL_CONDITION_SATISFIED 0x911C
#define GL_COMPUTE_SHADER 0x91B9 #define GL_COMPUTE_SHADER 0x91B9
#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 #define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310
#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 #define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311

View File

@ -93,6 +93,8 @@ PStatCollector CLP(GraphicsStateGuardian)::_texture_update_pcollector("Draw:Upda
PStatCollector CLP(GraphicsStateGuardian)::_fbo_bind_pcollector("Draw:Bind FBO"); PStatCollector CLP(GraphicsStateGuardian)::_fbo_bind_pcollector("Draw:Bind FBO");
PStatCollector CLP(GraphicsStateGuardian)::_check_error_pcollector("Draw:Check errors"); PStatCollector CLP(GraphicsStateGuardian)::_check_error_pcollector("Draw:Check errors");
PStatCollector CLP(GraphicsStateGuardian)::_check_residency_pcollector("*:PStats:Check residency"); PStatCollector CLP(GraphicsStateGuardian)::_check_residency_pcollector("*:PStats:Check residency");
PStatCollector CLP(GraphicsStateGuardian)::_wait_fence_pcollector("Wait:Fence");
PStatCollector CLP(GraphicsStateGuardian)::_copy_texture_finish_pcollector("Draw:Copy texture:Finish");
#if defined(HAVE_CG) && !defined(OPENGLES) #if defined(HAVE_CG) && !defined(OPENGLES)
AtomicAdjust::Integer CLP(GraphicsStateGuardian)::_num_gsgs_with_cg_contexts = 0; AtomicAdjust::Integer CLP(GraphicsStateGuardian)::_num_gsgs_with_cg_contexts = 0;
@ -164,6 +166,10 @@ null_glPolygonOffsetClamp(GLfloat factor, GLfloat units, GLfloat clamp) {
} }
#endif #endif
static void APIENTRY
null_glMemoryBarrier(GLbitfield barriers) {
}
#ifndef OPENGLES_1 #ifndef OPENGLES_1
// We have a default shader that will be applied when there isn't any shader // We have a default shader that will be applied when there isn't any shader
// applied (e.g. if it failed to compile). We need this because OpenGL ES // applied (e.g. if it failed to compile). We need this because OpenGL ES
@ -507,7 +513,9 @@ int CLP(GraphicsStateGuardian)::get_driver_shader_version_minor() { return _gl_s
CLP(GraphicsStateGuardian):: CLP(GraphicsStateGuardian)::
CLP(GraphicsStateGuardian)(GraphicsEngine *engine, GraphicsPipe *pipe) : CLP(GraphicsStateGuardian)(GraphicsEngine *engine, GraphicsPipe *pipe) :
GraphicsStateGuardian(gl_coordinate_system, engine, pipe), GraphicsStateGuardian(gl_coordinate_system, engine, pipe),
_renderbuffer_residency(get_prepared_objects()->get_name(), "renderbuffer") _renderbuffer_residency(get_prepared_objects()->get_name(), "renderbuffer"),
_active_ppbuffer_memory_pcollector("Graphics memory:" + get_prepared_objects()->get_name() + ":Active:ppbuffer"),
_inactive_ppbuffer_memory_pcollector("Graphics memory:" + get_prepared_objects()->get_name() + ":Inactive:ppbuffer")
{ {
_error_count = 0; _error_count = 0;
_last_error_check = -1.0; _last_error_check = -1.0;
@ -1685,13 +1693,18 @@ reset() {
if (is_at_least_gles_version(3, 0)) { if (is_at_least_gles_version(3, 0)) {
_glMapBufferRange = (PFNGLMAPBUFFERRANGEEXTPROC) _glMapBufferRange = (PFNGLMAPBUFFERRANGEEXTPROC)
get_extension_func("glMapBufferRange"); get_extension_func("glMapBufferRange");
_glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)
} else if (has_extension("GL_EXT_map_buffer_range")) { get_extension_func("glUnmapBuffer");
}
else if (has_extension("GL_EXT_map_buffer_range")) {
_glMapBufferRange = (PFNGLMAPBUFFERRANGEEXTPROC) _glMapBufferRange = (PFNGLMAPBUFFERRANGEEXTPROC)
get_extension_func("glMapBufferRangeEXT"); get_extension_func("glMapBufferRangeEXT");
_glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)
} else { get_extension_func("glUnmapBufferOES");
}
else {
_glMapBufferRange = nullptr; _glMapBufferRange = nullptr;
_glUnmapBuffer = nullptr;
} }
#else #else
// Check for various advanced buffer management features. // Check for various advanced buffer management features.
@ -2891,6 +2904,28 @@ reset() {
is_at_least_gl_version(3, 3) || has_extension("GL_ARB_blend_func_extended"); is_at_least_gl_version(3, 3) || has_extension("GL_ARB_blend_func_extended");
#endif #endif
#ifndef OPENGLES
if (is_at_least_gl_version(3, 2) || has_extension("GL_ARB_sync")) {
_glFenceSync = (PFNGLFENCESYNCPROC)get_extension_func("glFenceSync");
_glDeleteSync = (PFNGLDELETESYNCPROC)get_extension_func("glDeleteSync");
_glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)get_extension_func("glClientWaitSync");
_glGetSynciv = (PFNGLGETSYNCIVPROC)get_extension_func("glGetSynciv");
}
#elif !defined(OPENGLES_1)
if (is_at_least_gles_version(3, 0)) {
_glFenceSync = (PFNGLFENCESYNCPROC)get_extension_func("glFenceSync");
_glDeleteSync = (PFNGLDELETESYNCPROC)get_extension_func("glDeleteSync");
_glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)get_extension_func("glClientWaitSync");
_glGetSynciv = (PFNGLGETSYNCIVPROC)get_extension_func("glGetSynciv");
}
else if (has_extension("GL_APPLE_sync")) {
_glFenceSync = (PFNGLFENCESYNCPROC)get_extension_func("glFenceSyncAPPLE");
_glDeleteSync = (PFNGLDELETESYNCPROC)get_extension_func("glDeleteSyncAPPLE");
_glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)get_extension_func("glClientWaitSyncAPPLE");
_glGetSynciv = (PFNGLGETSYNCIVPROC)get_extension_func("glGetSyncivAPPLE");
}
#endif
#ifdef OPENGLES #ifdef OPENGLES
_edge_clamp = GL_CLAMP_TO_EDGE; _edge_clamp = GL_CLAMP_TO_EDGE;
#else #else
@ -3137,7 +3172,7 @@ reset() {
} else { } else {
_glBindImageTexture = nullptr; _glBindImageTexture = nullptr;
_glMemoryBarrier = nullptr; _glMemoryBarrier = null_glMemoryBarrier;
} }
#endif // !OPENGLES_1 #endif // !OPENGLES_1
@ -4162,6 +4197,10 @@ begin_frame(Thread *current_thread) {
_primitive_batches_display_list_pcollector.clear_level(); _primitive_batches_display_list_pcollector.clear_level();
#endif #endif
if (!_async_ram_copies.empty()) {
finish_async_framebuffer_ram_copies();
}
#if defined(DO_PSTATS) && !defined(OPENGLES) #if defined(DO_PSTATS) && !defined(OPENGLES)
int frame_number = ClockObject::get_global_clock()->get_frame_count(current_thread); int frame_number = ClockObject::get_global_clock()->get_frame_count(current_thread);
if (_current_frame_timing == nullptr || if (_current_frame_timing == nullptr ||
@ -4350,6 +4389,38 @@ end_frame(Thread *current_thread) {
} }
#endif // OPENGLES #endif // OPENGLES
#ifndef OPENGLES_1
if (!_deleted_buffers.empty()) {
GLuint *indices = (GLuint *)alloca(sizeof(GLuint *) * _deleted_buffers.size());
size_t num_indices = 0;
DeletedBuffers::iterator it = _deleted_buffers.begin();
while (it != _deleted_buffers.end()) {
DeletedBuffer &buffer = *it;
if (!_supports_buffer_storage && buffer._mapped_pointer != nullptr) {
_glBindBuffer(GL_PIXEL_PACK_BUFFER, buffer._index);
_glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
buffer._mapped_pointer = nullptr;
}
if (++buffer._age > 2) {
indices[num_indices++] = buffer._index;
it = _deleted_buffers.erase(it);
_inactive_ppbuffer_memory_pcollector.sub_level(buffer._size);
} else {
++it;
}
}
if (!_supports_buffer_storage) {
_glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
}
if (num_indices > 0) {
_glDeleteBuffers(num_indices, indices);
}
}
_active_ppbuffer_memory_pcollector.flush_level();
_inactive_ppbuffer_memory_pcollector.flush_level();
#endif
#ifndef NDEBUG #ifndef NDEBUG
if (_check_errors || (_supports_debug && gl_debug)) { if (_check_errors || (_supports_debug && gl_debug)) {
report_my_gl_errors(); report_my_gl_errors();
@ -6573,6 +6644,72 @@ record_deleted_display_list(GLuint index) {
_deleted_display_lists.push_back(index); _deleted_display_lists.push_back(index);
} }
#ifndef OPENGLES_1
/**
* Creates a new buffer for client access. It is bound when this returns.
* If persistent mapping is possible, mapped_ptr will be filled in with a
* pointer to the mapped data.
*/
void CLP(GraphicsStateGuardian)::
bind_new_client_buffer(GLuint &index, void *&mapped_ptr, GLenum target, size_t size) {
_active_ppbuffer_memory_pcollector.add_level(size);
{
// Start at the end, because removing near the end is cheaper.
LightMutexHolder holder(_lock);
size_t i = _deleted_buffers.size();
while (i > 1) {
--i;
DeletedBuffer &buffer = _deleted_buffers[i];
if (buffer._size == size) {
index = buffer._index;
mapped_ptr = buffer._mapped_pointer;
_glBindBuffer(target, buffer._index);
if (!_supports_buffer_storage && mapped_ptr != nullptr) {
// Need to unmap it before we can use it.
_glUnmapBuffer(target);
mapped_ptr = nullptr;
}
_deleted_buffers.erase(_deleted_buffers.begin() + i);
_inactive_ppbuffer_memory_pcollector.sub_level(size);
return;
}
}
}
_glGenBuffers(1, &index);
_glBindBuffer(target, index);
#ifndef OPENGLES
if (_supports_buffer_storage) {
// Map persistently, we already use fences to synchronize access anyway.
_glBufferStorage(target, size, nullptr, GL_MAP_READ_BIT |
GL_CLIENT_STORAGE_BIT | GL_MAP_PERSISTENT_BIT);
mapped_ptr = _glMapBufferRange(target, 0, size,
GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT);
} else
#endif
{
//XXX does it matter what usage hint we pass here? None seem to fit well.
_glBufferData(target, size, nullptr, GL_DYNAMIC_DRAW);
mapped_ptr = nullptr;
}
}
/**
* Called when the given buffer, as returned by bind_new_client_buffer, is no
* longer needed.
*/
void CLP(GraphicsStateGuardian)::
release_client_buffer(GLuint index, void *mapped_ptr, size_t size) {
// This may be called from any thread, so we can't make OpenGL calls here
// (like unmapping the buffer).
LightMutexHolder holder(_lock);
_deleted_buffers.push_back({index, 0, mapped_ptr, size});
_active_ppbuffer_memory_pcollector.sub_level(size);
_inactive_ppbuffer_memory_pcollector.add_level(size);
}
#endif // !OPENGLES_1
/** /**
* Creates a new retained-mode representation of the given data, and returns a * Creates a new retained-mode representation of the given data, and returns a
* newly-allocated VertexBufferContext pointer to reference it. It is the * newly-allocated VertexBufferContext pointer to reference it. It is the
@ -7567,7 +7704,6 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z,
return true; return true;
} }
/** /**
* Copy the pixels within the indicated display region from the framebuffer * Copy the pixels within the indicated display region from the framebuffer
* into system memory, not texture memory. Returns true on success, false on * into system memory, not texture memory. Returns true on success, false on
@ -7577,7 +7713,8 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z,
*/ */
bool CLP(GraphicsStateGuardian):: bool CLP(GraphicsStateGuardian)::
framebuffer_copy_to_ram(Texture *tex, int view, int z, framebuffer_copy_to_ram(Texture *tex, int view, int z,
const DisplayRegion *dr, const RenderBuffer &rb) { const DisplayRegion *dr, const RenderBuffer &rb,
ScreenshotRequest *request) {
nassertr(tex != nullptr && dr != nullptr, false); nassertr(tex != nullptr && dr != nullptr, false);
set_read_buffer(rb._buffer_type); set_read_buffer(rb._buffer_type);
glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_PACK_ALIGNMENT, 1);
@ -7868,12 +8005,23 @@ framebuffer_copy_to_ram(Texture *tex, int view, int z,
} }
#endif // NDEBUG #endif // NDEBUG
unsigned char *image_ptr = tex->modify_ram_image(); size_t image_size = tex->get_expected_ram_page_size();
size_t image_size = tex->get_ram_image_size(); unsigned char *image_ptr = nullptr;
if (z >= 0 || view > 0) { #ifndef OPENGLES_1
image_size = tex->get_expected_ram_page_size(); GLuint pbo = 0;
void *mapped_ptr = nullptr;
if (request != nullptr) {
nassertr(z <= 0, false);
image_size *= tex->get_z_size();
bind_new_client_buffer(pbo, mapped_ptr, GL_PIXEL_PACK_BUFFER, image_size);
} else
#endif
{
image_ptr = tex->modify_ram_image();
if (z >= 0) { if (z >= 0) {
image_ptr += z * image_size; image_ptr += z * image_size;
} else {
image_size = tex->get_ram_image_size();
} }
if (view > 0) { if (view > 0) {
image_ptr += (view * tex->get_z_size()) * image_size; image_ptr += (view * tex->get_z_size()) * image_size;
@ -7884,9 +8032,22 @@ framebuffer_copy_to_ram(Texture *tex, int view, int z,
glReadPixels(xo, yo, w, h, external_format, glReadPixels(xo, yo, w, h, external_format,
get_component_type(component_type), image_ptr); get_component_type(component_type), image_ptr);
// We may have to reverse the byte ordering of the image if GL didn't do it #ifndef OPENGLES_1
// for us. if (request != nullptr) {
_glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
#ifndef OPENGLES
if (_supports_buffer_storage) {
_glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT);
}
#endif
GLsync fence = _glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
_async_ram_copies.push_back({request, pbo, fence, external_format,
view, mapped_ptr, image_size});
} else
#endif
if (external_format == GL_RGBA || external_format == GL_RGB) { if (external_format == GL_RGBA || external_format == GL_RGB) {
// We may have to reverse the byte ordering of the image if GL didn't do it
// for us.
PTA_uchar new_image; PTA_uchar new_image;
const unsigned char *result = const unsigned char *result =
fix_component_ordering(new_image, image_ptr, image_size, fix_component_ordering(new_image, image_ptr, image_size,
@ -7896,10 +8057,114 @@ framebuffer_copy_to_ram(Texture *tex, int view, int z,
} }
} }
#ifdef OPENGLES_1
if (request != nullptr) {
request->finish();
}
#endif
report_my_gl_errors(); report_my_gl_errors();
return true; return true;
} }
/**
* Finishes all asynchronous framebuffer-copy-to-ram operations.
*/
void CLP(GraphicsStateGuardian)::
finish_async_framebuffer_ram_copies(bool force) {
#ifndef OPENGLES_1
if (_async_ram_copies.empty()) {
return;
}
//XXX having a fixed number of threads is not a great idea. We ought to have
// a common thread pool that is sized based on the available number of CPUs.
#ifdef HAVE_THREADS
AsyncTaskManager *task_mgr = AsyncTaskManager::get_global_ptr();
static AsyncTaskChain *chain = task_mgr->make_task_chain("texture_download", 2, TP_low);
#endif
PStatTimer timer(_copy_texture_finish_pcollector);
if (force) {
// Just wait for the last fence, the rest must be complete too then.
PStatTimer timer(_wait_fence_pcollector);
GLsync fence = _async_ram_copies.back()._fence;
_glClientWaitSync(fence, 0, (GLuint64)-1);
}
while (!_async_ram_copies.empty()) {
AsyncRamCopy &copy = _async_ram_copies.front();
if (!force) {
GLenum result = _glClientWaitSync(copy._fence, 0, 0);
if (result != GL_ALREADY_SIGNALED && result != GL_CONDITION_SATISFIED) {
// Not yet done. The rest must not yet be done then, either.
break;
}
}
_glDeleteSync(copy._fence);
GLuint pbo = copy._pbo;
int view = copy._view;
PT(ScreenshotRequest) request = std::move(copy._request);
GLuint external_format = copy._external_format;
void *mapped_ptr = copy._mapped_pointer;
size_t size = copy._size;
if (mapped_ptr == nullptr) {
_glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
#ifdef OPENGLES
// There is neither glMapBuffer nor persistent mapping in OpenGL ES
mapped_ptr = _glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, size, GL_MAP_READ_BIT);
#else
// If we get here in desktop GL, we must not have persistent mapping
nassertv(!_supports_buffer_storage);
mapped_ptr = _glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
#endif
}
// Do the memcpy in the background, since it can be slow.
auto func = [=](AsyncTask *task) {
const unsigned char *result = (unsigned char *)mapped_ptr;
PTA_uchar new_image;
if (external_format == GL_RGBA || external_format == GL_RGB) {
// We may have to reverse the byte ordering of the image if GL didn't do
// it for us.
result = fix_component_ordering(new_image, result, size,
external_format, request->get_result());
}
request->set_view_data(view, result);
// Finishing can take a long time, release the client buffer first so it
// can be reused for the next screenshot.
this->release_client_buffer(pbo, mapped_ptr, size);
request->finish();
return AsyncTask::DS_done;
};
#ifdef HAVE_THREADS
// We assign a sort value based on the originating frame number, so that
// earlier frames will be processed before subsequent frames, but we don't
// make it unique for every frame, which would kill concurrency.
int frame_number = request->get_frame_number();
chain->add(std::move(func), "screenshot", frame_number >> 3, -(frame_number & ((1 << 3) - 1)));
#else
func(nullptr);
#endif
_async_ram_copies.pop_front();
// If there is 1 remaining, save it for next frame. This helps prevent an
// inconsistent frame rate when the number of fetched frames alternates
// between 0 and 2, which can settle into a stable feedback loop.
if (!force && _async_ram_copies.size() == 1) {
break;
}
}
_glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
#endif
}
#ifdef SUPPORT_FIXED_FUNCTION #ifdef SUPPORT_FIXED_FUNCTION
/** /**
* *

View File

@ -142,6 +142,7 @@ typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *a
typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays);
typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha); typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha);
typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target);
#ifndef OPENGLES_1 #ifndef OPENGLES_1
// GLSL shader functions // GLSL shader functions
@ -231,6 +232,13 @@ typedef void (APIENTRYP PFNGLBUFFERSTORAGEPROC) (GLenum target, GLsizeiptr size,
typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC) (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC) (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format);
typedef void (APIENTRYP PFNGLCLEARTEXIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); typedef void (APIENTRYP PFNGLCLEARTEXIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, const void *data);
typedef void (APIENTRYP PFNGLCLEARTEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); typedef void (APIENTRYP PFNGLCLEARTEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data);
typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC) (GLenum condition, GLbitfield flags);
typedef GLboolean (APIENTRYP PFNGLISSYNCPROC) (GLsync sync);
typedef void (APIENTRYP PFNGLDELETESYNCPROC) (GLsync sync);
typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout);
typedef void (APIENTRYP PFNGLWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout);
typedef void (APIENTRYP PFNGLGETINTEGER64VPROC) (GLenum pname, GLint64 *data);
typedef void (APIENTRYP PFNGLGETSYNCIVPROC) (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values);
#endif // OPENGLES_1 #endif // OPENGLES_1
#ifndef OPENGLES #ifndef OPENGLES
typedef void (APIENTRYP PFNGLBINDTEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); typedef void (APIENTRYP PFNGLBINDTEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures);
@ -253,7 +261,6 @@ typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64PROC) (GLuint index, GLuint64EXT
typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VPROC) (GLuint index, const GLuint64EXT *v); typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VPROC) (GLuint index, const GLuint64EXT *v);
typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VPROC) (GLuint index, GLenum pname, GLuint64EXT *params); typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VPROC) (GLuint index, GLenum pname, GLuint64EXT *params);
typedef void *(APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access); typedef void *(APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access);
typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target);
typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, void *data); typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, void *data);
#endif // OPENGLES #endif // OPENGLES
#endif // __EDG__ #endif // __EDG__
@ -353,6 +360,11 @@ public:
virtual ShaderContext *prepare_shader(Shader *shader); virtual ShaderContext *prepare_shader(Shader *shader);
virtual void release_shader(ShaderContext *sc); virtual void release_shader(ShaderContext *sc);
#ifndef OPENGLES_1
void bind_new_client_buffer(GLuint &index, void *&mapped_ptr, GLenum target, size_t size);
void release_client_buffer(GLuint index, void *mapped_ptr, size_t size);
#endif
void record_deleted_display_list(GLuint index); void record_deleted_display_list(GLuint index);
virtual VertexBufferContext *prepare_vertex_buffer(GeomVertexArrayData *data); virtual VertexBufferContext *prepare_vertex_buffer(GeomVertexArrayData *data);
@ -403,7 +415,9 @@ public:
virtual bool framebuffer_copy_to_texture virtual bool framebuffer_copy_to_texture
(Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb); (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb);
virtual bool framebuffer_copy_to_ram virtual bool framebuffer_copy_to_ram
(Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb); (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb,
ScreenshotRequest *request);
void finish_async_framebuffer_ram_copies(bool force = false);
#ifdef SUPPORT_FIXED_FUNCTION #ifdef SUPPORT_FIXED_FUNCTION
void apply_fog(Fog *fog); void apply_fog(Fog *fog);
@ -880,6 +894,7 @@ public:
#ifdef OPENGLES #ifdef OPENGLES
PFNGLMAPBUFFERRANGEEXTPROC _glMapBufferRange; PFNGLMAPBUFFERRANGEEXTPROC _glMapBufferRange;
PFNGLUNMAPBUFFEROESPROC _glUnmapBuffer;
#else #else
PFNGLMAPBUFFERRANGEPROC _glMapBufferRange; PFNGLMAPBUFFERRANGEPROC _glMapBufferRange;
#endif #endif
@ -891,6 +906,8 @@ public:
bool _supports_buffer_storage; bool _supports_buffer_storage;
PFNGLBUFFERSTORAGEPROC _glBufferStorage; PFNGLBUFFERSTORAGEPROC _glBufferStorage;
#else
static const bool _supports_buffer_storage = false;
#endif #endif
bool _supports_blend_equation_separate; bool _supports_blend_equation_separate;
@ -1088,6 +1105,13 @@ public:
PFNGLSHADERSTORAGEBLOCKBINDINGPROC _glShaderStorageBlockBinding; PFNGLSHADERSTORAGEBLOCKBINDINGPROC _glShaderStorageBlockBinding;
#endif // !OPENGLES #endif // !OPENGLES
#ifndef OPENGLES_1
PFNGLFENCESYNCPROC _glFenceSync;
PFNGLDELETESYNCPROC _glDeleteSync;
PFNGLCLIENTWAITSYNCPROC _glClientWaitSync;
PFNGLGETSYNCIVPROC _glGetSynciv;
#endif
GLenum _edge_clamp; GLenum _edge_clamp;
GLenum _border_clamp; GLenum _border_clamp;
GLenum _mirror_repeat; GLenum _mirror_repeat;
@ -1109,6 +1133,17 @@ public:
DeletedNames _deleted_display_lists; DeletedNames _deleted_display_lists;
DeletedNames _deleted_queries; DeletedNames _deleted_queries;
#ifndef OPENGLES_1
struct DeletedBuffer {
GLuint _index;
int _age;
void *_mapped_pointer;
size_t _size;
};
typedef pvector<DeletedBuffer> DeletedBuffers;
DeletedBuffers _deleted_buffers;
#endif
#ifndef OPENGLES_1 #ifndef OPENGLES_1
// Stores textures for which memory bariers should be issued. // Stores textures for which memory bariers should be issued.
typedef pset<TextureContext*> TextureSet; typedef pset<TextureContext*> TextureSet;
@ -1165,8 +1200,22 @@ public:
FrameTiming *_current_frame_timing = nullptr; FrameTiming *_current_frame_timing = nullptr;
#endif #endif
struct AsyncRamCopy {
PT(ScreenshotRequest) _request;
GLuint _pbo;
GLsync _fence;
GLuint _external_format;
int _view;
void *_mapped_pointer;
size_t _size;
};
pdeque<AsyncRamCopy> _async_ram_copies;
BufferResidencyTracker _renderbuffer_residency; BufferResidencyTracker _renderbuffer_residency;
PStatCollector _active_ppbuffer_memory_pcollector;
PStatCollector _inactive_ppbuffer_memory_pcollector;
static PStatCollector _load_display_list_pcollector; static PStatCollector _load_display_list_pcollector;
static PStatCollector _primitive_batches_display_list_pcollector; static PStatCollector _primitive_batches_display_list_pcollector;
static PStatCollector _vertices_display_list_pcollector; static PStatCollector _vertices_display_list_pcollector;
@ -1177,6 +1226,8 @@ public:
static PStatCollector _fbo_bind_pcollector; static PStatCollector _fbo_bind_pcollector;
static PStatCollector _check_error_pcollector; static PStatCollector _check_error_pcollector;
static PStatCollector _check_residency_pcollector; static PStatCollector _check_residency_pcollector;
static PStatCollector _wait_fence_pcollector;
static PStatCollector _copy_texture_finish_pcollector;
public: public:
virtual TypeHandle get_type() const { virtual TypeHandle get_type() const {

View File

@ -28,6 +28,7 @@ class RenderBuffer;
class GraphicsWindow; class GraphicsWindow;
class NodePath; class NodePath;
class GraphicsOutputBase; class GraphicsOutputBase;
class ScreenshotRequest;
class VertexBufferContext; class VertexBufferContext;
class IndexBufferContext; class IndexBufferContext;
@ -220,7 +221,8 @@ public:
virtual bool framebuffer_copy_to_texture virtual bool framebuffer_copy_to_texture
(Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb)=0; (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb)=0;
virtual bool framebuffer_copy_to_ram virtual bool framebuffer_copy_to_ram
(Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb)=0; (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb,
ScreenshotRequest *request = nullptr)=0;
virtual CoordinateSystem get_internal_coordinate_system() const=0; virtual CoordinateSystem get_internal_coordinate_system() const=0;

View File

@ -1393,8 +1393,8 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z,
*/ */
bool TinyGraphicsStateGuardian:: bool TinyGraphicsStateGuardian::
framebuffer_copy_to_ram(Texture *tex, int view, int z, framebuffer_copy_to_ram(Texture *tex, int view, int z,
const DisplayRegion *dr, const DisplayRegion *dr, const RenderBuffer &rb,
const RenderBuffer &rb) { ScreenshotRequest *request) {
nassertr(tex != nullptr && dr != nullptr, false); nassertr(tex != nullptr && dr != nullptr, false);
int xo, yo, w, h; int xo, yo, w, h;
@ -1465,6 +1465,9 @@ framebuffer_copy_to_ram(Texture *tex, int view, int z,
fo += _c->zb->linesize / PSZB; fo += _c->zb->linesize / PSZB;
} }
if (request != nullptr) {
request->finish();
}
return true; return true;
} }

View File

@ -78,7 +78,8 @@ public:
virtual bool framebuffer_copy_to_texture virtual bool framebuffer_copy_to_texture
(Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb); (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb);
virtual bool framebuffer_copy_to_ram virtual bool framebuffer_copy_to_ram
(Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb); (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb,
ScreenshotRequest *request);
virtual void set_state_and_transform(const RenderState *state, virtual void set_state_and_transform(const RenderState *state,
const TransformState *transform); const TransformState *transform);