Kaynağa Gözat

glgsg: Support updating SSBO data from the CPU (only UH_dynamic)

rdb 4 hafta önce
ebeveyn
işleme
a9aa25577a

+ 22 - 0
panda/src/display/graphicsEngine.cxx

@@ -1161,6 +1161,28 @@ extract_shader_buffer_data(ShaderBuffer *buffer, GraphicsStateGuardian *gsg,
   });
 }
 
+/**
+ * Updates shader buffer data from the CPU.  This may be a subsection of the
+ * buffer.  Returns true on success.
+ *
+ * Updating is only allowed for buffers with usage hint set to UH_dynamic.
+ */
+bool GraphicsEngine::
+update_shader_buffer_data(ShaderBuffer *buffer, GraphicsStateGuardian *gsg,
+                          const vector_uchar &data, size_t offset) {
+  if (buffer->get_usage_hint() != GeomEnums::UH_dynamic) {
+    display_cat.error()
+      << "Cannot update ShaderBuffer with usage hint " << buffer->get_usage_hint() << "\n";
+    return false;
+  }
+
+  const unsigned char *data_ptr = data.data();
+  const size_t data_size = data.size();
+  return run_on_draw_thread([=] () {
+    return gsg->update_shader_buffer_data(buffer, offset, data_size, data_ptr);
+  });
+}
+
 /**
  * Asks the indicated GraphicsStateGuardian to dispatch the compute shader in
  * the given ShaderAttrib using the given work group counts.  This can act as

+ 3 - 0
panda/src/display/graphicsEngine.h

@@ -117,6 +117,9 @@ PUBLISHED:
   bool extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg);
   vector_uchar extract_shader_buffer_data(ShaderBuffer *buffer, GraphicsStateGuardian *gsg,
                                           size_t start = 0, size_t size = (size_t)-1);
+  bool update_shader_buffer_data(ShaderBuffer *buffer, GraphicsStateGuardian *gsg,
+                                 const vector_uchar &data, size_t offset = 0);
+
   void dispatch_compute(const LVecBase3i &work_groups,
                         const RenderState *state,
                         GraphicsStateGuardian *gsg);

+ 13 - 0
panda/src/display/graphicsStateGuardian.cxx

@@ -766,6 +766,19 @@ release_shader_buffers(const pvector<BufferContext *> &contexts) {
   }
 }
 
+/**
+ * This method should only be called by the GraphicsEngine.  Do not call it
+ * directly; call GraphicsEngine::update_shader_buffer_data() instead.
+ *
+ * This method will be called in the draw thread to upload data to (a part of)
+ * the shader buffer from the CPU.
+ */
+bool GraphicsStateGuardian::
+update_shader_buffer_data(ShaderBuffer *buffer, size_t start, size_t size,
+                          const unsigned char *data) {
+  return false;
+}
+
 /**
  * This method should only be called by the GraphicsEngine.  Do not call it
  * directly; call GraphicsEngine::extract_texture_data() instead.

+ 2 - 0
panda/src/display/graphicsStateGuardian.h

@@ -326,6 +326,8 @@ public:
   virtual BufferContext *prepare_shader_buffer(ShaderBuffer *data);
   virtual void release_shader_buffer(BufferContext *ibc);
   virtual void release_shader_buffers(const pvector<BufferContext *> &contexts);
+  virtual bool update_shader_buffer_data(ShaderBuffer *buffer, size_t start,
+                                         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);
 

+ 40 - 0
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -2834,6 +2834,8 @@ reset() {
         get_extension_func("glMapNamedBufferRange");
     }
 
+    _glNamedBufferSubData = (PFNGLNAMEDBUFFERSUBDATAPROC)
+      get_extension_func("glNamedBufferSubData");
     _glGetNamedBufferSubData = (PFNGLGETNAMEDBUFFERSUBDATAPROC)
       get_extension_func("glGetNamedBufferSubData");
 
@@ -7613,6 +7615,9 @@ prepare_shader_buffer(ShaderBuffer *data) {
       if (data->get_usage_hint() == GeomEnums::UH_client) {
         flags |= GL_CLIENT_STORAGE_BIT;
       }
+      if (data->get_usage_hint() == GeomEnums::UH_dynamic) {
+        flags |= GL_DYNAMIC_STORAGE_BIT;
+      }
       _glBufferStorage(GL_SHADER_STORAGE_BUFFER, num_bytes, data->get_initial_data(), flags);
     } else {
       _glBufferData(GL_SHADER_STORAGE_BUFFER, num_bytes, data->get_initial_data(), get_usage(data->get_usage_hint()));
@@ -7761,6 +7766,41 @@ release_shader_buffers(const pvector<BufferContext *> &contexts) {
   report_my_gl_errors();
 }
 
+/**
+ * This method should only be called by the GraphicsEngine.  Do not call it
+ * directly; call GraphicsEngine::update_shader_buffer_data() instead.
+ *
+ * This method will be called in the draw thread to upload data to (a part of)
+ * the shader buffer from the CPU.
+ */
+bool CLP(GraphicsStateGuardian)::
+update_shader_buffer_data(ShaderBuffer *buffer, size_t start, size_t size,
+                          const unsigned char *data) {
+  nassertr(buffer->get_usage_hint() == GeomEnums::UH_dynamic, false);
+
+  BufferContext *bc = buffer->prepare_now(get_prepared_objects(), this);
+  if (bc == nullptr || !bc->is_of_type(CLP(BufferContext)::get_class_type())) {
+    return false;
+  }
+  CLP(BufferContext) *gbc = DCAST(CLP(BufferContext), bc);
+
+  if (_glMemoryBarrier != nullptr) {
+    _glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
+  }
+
+  if (_supports_dsa) {
+    _glNamedBufferSubData(gbc->_index, start, size, data);
+  } else {
+    _glBindBuffer(GL_SHADER_STORAGE_BUFFER, gbc->_index);
+    _glBufferSubData(GL_SHADER_STORAGE_BUFFER, start, size, data);
+    _glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
+    _current_sbuffer_index = 0;
+  }
+  report_my_gl_errors();
+
+  return false;
+}
+
 /**
  * This method should only be called by the GraphicsEngine.  Do not call it
  * directly; call GraphicsEngine::extract_texture_data() instead.

+ 3 - 0
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -399,6 +399,8 @@ public:
   CLP(BufferContext) *apply_shader_buffer(GLuint base, ShaderBuffer *buffer);
   virtual void release_shader_buffer(BufferContext *bc);
   virtual void release_shader_buffers(const pvector<BufferContext *> &contexts);
+  virtual bool update_shader_buffer_data(ShaderBuffer *buffer, size_t start,
+                                         size_t size, const unsigned char *data);
   virtual bool extract_shader_buffer_data(ShaderBuffer *buffer, vector_uchar &data,
                                           size_t start, size_t size);
 #endif
@@ -1019,6 +1021,7 @@ public:
   PFNGLGENERATETEXTUREMIPMAPPROC _glGenerateTextureMipmap;
   PFNGLBINDTEXTUREUNITPROC _glBindTextureUnit;
   PFNGLMAPNAMEDBUFFERRANGEPROC _glMapNamedBufferRange;
+  PFNGLNAMEDBUFFERSUBDATAPROC _glNamedBufferSubData;
   PFNGLGETNAMEDBUFFERSUBDATAPROC _glGetNamedBufferSubData;
 #endif
 

+ 15 - 2
tests/display/test_glsl_shader.py

@@ -302,8 +302,8 @@ def test_glsl_ssbo(gsg):
     from struct import pack, unpack
     num1 = pack('<i', 1234567)
     num2 = pack('<ii', -1234567, 867451)
-    buffer1 = core.ShaderBuffer("buffer1", num1, core.GeomEnums.UH_static)
-    buffer2 = core.ShaderBuffer("buffer2", num2, core.GeomEnums.UH_static)
+    buffer1 = core.ShaderBuffer("buffer1", num1, core.GeomEnums.UH_dynamic)
+    buffer2 = core.ShaderBuffer("buffer2", num2, core.GeomEnums.UH_dynamic)
 
     preamble = """
     layout(std430, binding=0) buffer buffer1 {
@@ -335,6 +335,19 @@ def test_glsl_ssbo(gsg):
     data3 = gsg.get_engine().extract_shader_buffer_data(buffer2, gsg, 4, 8)
     assert unpack('<i', data3[:4]) == (867451, )
 
+    gsg.get_engine().update_shader_buffer_data(buffer1, gsg, pack('<i', 92573))
+    gsg.get_engine().update_shader_buffer_data(buffer2, gsg, pack('<i', 64515), 4)
+
+    code = """
+    assert(value1 == 92573);
+    assert(value2 == 5343525);
+    assert(value3 == 64515);
+    """
+    run_glsl_test(gsg, code, preamble, {'buffer1': buffer1, 'buffer2': buffer2},
+                  exts={'GL_ARB_shader_storage_buffer_object',
+                        'GL_ARB_uniform_buffer_object',
+                        'GL_ARB_shading_language_420pack'})
+
 
 def test_glsl_int(gsg):
     inputs = dict(