diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 11ec2540dd..394aa39731 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -792,6 +792,16 @@ extract_shader_buffer_data(ShaderBuffer *buffer, vector_uchar &data, return false; } +/** + * Asynchronous version of extract_shader_buffer_data. It is the caller's + * responsibility that the data argument outlasts the token. + */ +void GraphicsStateGuardian:: +async_extract_shader_buffer_data(ShaderBuffer *buffer, vector_uchar &data, + size_t start, size_t size, CompletionToken token) { + token.complete(extract_shader_buffer_data(buffer, data, start, size)); +} + /** * Begins a new occlusion query. After this call, you may call * begin_draw_primitives() and draw_triangles()/draw_whatever() repeatedly. diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index 36ffcb0960..8d62382f18 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -330,6 +330,9 @@ public: size_t size, const unsigned char *data); virtual bool extract_shader_buffer_data(ShaderBuffer *buffer, vector_uchar &data, size_t start = 0, size_t size = (size_t)-1); + virtual void async_extract_shader_buffer_data(ShaderBuffer *buffer, vector_uchar &data, + size_t start = 0, size_t size = (size_t)-1, + CompletionToken token = CompletionToken()); virtual void begin_occlusion_query(); virtual PT(OcclusionQueryContext) end_occlusion_query(); diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 61ec11513d..3c72075525 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -7843,6 +7843,60 @@ extract_shader_buffer_data(ShaderBuffer *buffer, vector_uchar &data, } #endif +#ifndef OPENGLES +/** + * Asynchronous version of extract_shader_buffer_data. It is the caller's + * responsibility that the data argument outlasts the token. + */ +void CLP(GraphicsStateGuardian):: +async_extract_shader_buffer_data(ShaderBuffer *buffer, vector_uchar &data, + size_t start, size_t size, CompletionToken token) { + BufferContext *bc = buffer->prepare_now(get_prepared_objects(), this); + if (bc == nullptr || !bc->is_of_type(CLP(BufferContext)::get_class_type())) { + token.complete(false); + return; + } + + CLP(BufferContext) *gbc = DCAST(CLP(BufferContext), bc); + + size_t total_size = buffer->get_data_size_bytes(); + if (start >= total_size) { + data.clear(); + token.complete(true); + return; + } + + size = std::min(total_size - start, size); + + GLuint staging_buffer = 0; + void *mapped_ptr = nullptr; + + bind_new_client_buffer(staging_buffer, mapped_ptr, GL_COPY_WRITE_BUFFER, size); + if (_glMemoryBarrier != nullptr) { + _glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); + } + + _glBindBuffer(GL_SHADER_STORAGE_BUFFER, gbc->_index); + _glCopyBufferSubData(GL_SHADER_STORAGE_BUFFER, GL_COPY_WRITE_BUFFER, start, 0, size); + _glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + _glBindBuffer(GL_COPY_READ_BUFFER, 0); + _current_sbuffer_index = 0; + + if (_supports_buffer_storage && _glMemoryBarrier != nullptr) { + _glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT); + } + + insert_fence([=, &data, token = std::move(token)] (bool success) mutable { + if (success) { + data = vector_uchar((const unsigned char *)mapped_ptr, (const unsigned char *)mapped_ptr + size); + token.complete(true); + } else { + token.complete(false); + } + }); +} +#endif + #ifndef OPENGLES /** * Begins a new occlusion query. After this call, you may call diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index bd0eff59d3..eb5233e701 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -403,6 +403,9 @@ public: size_t size, const unsigned char *data); virtual bool extract_shader_buffer_data(ShaderBuffer *buffer, vector_uchar &data, size_t start, size_t size); + virtual void async_extract_shader_buffer_data(ShaderBuffer *buffer, vector_uchar &data, + size_t start = 0, size_t size = (size_t)-1, + CompletionToken token = CompletionToken()); #endif #ifndef OPENGLES diff --git a/panda/src/gobj/preparedGraphicsObjects.cxx b/panda/src/gobj/preparedGraphicsObjects.cxx index 3089537dd2..927c5f2111 100644 --- a/panda/src/gobj/preparedGraphicsObjects.cxx +++ b/panda/src/gobj/preparedGraphicsObjects.cxx @@ -1380,6 +1380,15 @@ prepare_shader_buffer_now(ShaderBuffer *data, GraphicsStateGuardianBase *gsg) { return bc; } +/** + * Schedules the given function to be called during the next begin_frame(). + */ +void PreparedGraphicsObjects:: +enqueue_call(EnqueuedCall &&call) { + ReMutexHolder holder(_lock); + _enqueued_calls.push_back(std::move(call)); +} + /** * Creates a new future for the given object. */ @@ -1601,6 +1610,12 @@ begin_frame(GraphicsStateGuardianBase *gsg, Thread *current_thread) { } _enqueued_shader_buffers.clear(); + + for (EnqueuedCall &call : _enqueued_calls) { + std::move(call)(gsg); + } + + _enqueued_calls.clear(); } /** diff --git a/panda/src/gobj/preparedGraphicsObjects.h b/panda/src/gobj/preparedGraphicsObjects.h index fac1ab630d..f47d3a9275 100644 --- a/panda/src/gobj/preparedGraphicsObjects.h +++ b/panda/src/gobj/preparedGraphicsObjects.h @@ -31,6 +31,10 @@ #include "adaptiveLru.h" #include "asyncFuture.h" +#ifndef CPPPARSER +#include +#endif + class TextureContext; class SamplerContext; class GeomContext; @@ -160,6 +164,12 @@ PUBLISHED: GraphicsStateGuardianBase *gsg); public: +#ifndef CPPPARSER + typedef std::function EnqueuedCall; + + void enqueue_call(EnqueuedCall &&call); +#endif + /** * This is a handle to an enqueued object, from which the result can be * obtained upon completion. @@ -281,6 +291,7 @@ private: Buffers _prepared_shader_buffers; pvector _released_shader_buffers; EnqueuedShaderBuffers _enqueued_shader_buffers; + pvector _enqueued_calls; BufferCache _vertex_buffer_cache; BufferCacheLRU _vertex_buffer_cache_lru; diff --git a/panda/src/gobj/shaderBuffer.cxx b/panda/src/gobj/shaderBuffer.cxx index 9164a870ae..8872b64287 100644 --- a/panda/src/gobj/shaderBuffer.cxx +++ b/panda/src/gobj/shaderBuffer.cxx @@ -32,6 +32,26 @@ output(std::ostream &out) const { out << "buffer " << get_name() << ", " << _data_size_bytes << "B, " << _usage_hint; } +/** + * Asynchronously requests to read the given subset of the data on the CPU. + */ +PT(AsyncFuture) ShaderBuffer:: +extract_data(PreparedGraphicsObjects *prepared_objects, size_t start, size_t size) { + PT(AsyncFuture) future = new AsyncFuture; + PT(ParamBytes) bytes = new ParamBytes(vector_uchar()); + + prepared_objects->enqueue_call([=, bytes = std::move(bytes)] (GraphicsStateGuardianBase *gsg) { + gsg->async_extract_shader_buffer_data(this, (vector_uchar &)bytes->get_value(), start, size, [future = std::move(future), bytes = std::move(bytes)] (bool success) { + if (success) { + future->set_result(bytes); + } else { + future->set_result(nullptr); + } + }); + }); + return future; +} + /** * Indicates that the data should be enqueued to be prepared in the indicated * prepared_objects at the beginning of the next frame. This will ensure the diff --git a/panda/src/gobj/shaderBuffer.h b/panda/src/gobj/shaderBuffer.h index d523e13256..8cc9eedf90 100644 --- a/panda/src/gobj/shaderBuffer.h +++ b/panda/src/gobj/shaderBuffer.h @@ -21,6 +21,7 @@ #include "graphicsStateGuardianBase.h" #include "factoryParams.h" #include "vector_uchar.h" +#include "asyncFuture.h" class BufferContext; class PreparedGraphicsObjects; @@ -51,6 +52,9 @@ PUBLISHED: MAKE_PROPERTY(data_size_bytes, get_data_size_bytes); MAKE_PROPERTY(usage_hint, get_usage_hint); + PT(AsyncFuture) extract_data(PreparedGraphicsObjects *prepared_objects, + size_t start = 0, size_t size = (size_t)-1); + void prepare(PreparedGraphicsObjects *prepared_objects); bool is_prepared(PreparedGraphicsObjects *prepared_objects) const; diff --git a/panda/src/gsgbase/graphicsStateGuardianBase.h b/panda/src/gsgbase/graphicsStateGuardianBase.h index d49f144046..c3fac9a003 100644 --- a/panda/src/gsgbase/graphicsStateGuardianBase.h +++ b/panda/src/gsgbase/graphicsStateGuardianBase.h @@ -23,6 +23,7 @@ #include "patomic.h" #include "small_vector.h" #include "completionToken.h" +#include "vector_uchar.h" // A handful of forward references. @@ -32,6 +33,7 @@ class GraphicsWindow; class NodePath; class GraphicsOutputBase; class ScreenshotRequest; +class AsyncFuture; class VertexBufferContext; class IndexBufferContext; @@ -175,6 +177,9 @@ public: virtual BufferContext *prepare_shader_buffer(ShaderBuffer *data)=0; virtual void release_shader_buffer(BufferContext *ibc)=0; virtual void release_shader_buffers(const pvector &contexts)=0; + virtual void async_extract_shader_buffer_data(ShaderBuffer *buffer, vector_uchar &data, + size_t start = 0, size_t size = (size_t)-1, + CompletionToken token = CompletionToken())=0; virtual void dispatch_compute(int size_x, int size_y, int size_z)=0;