diff --git a/panda/src/display/graphicsStateGuardian.I b/panda/src/display/graphicsStateGuardian.I index ea3a9b61ac..15f95a19f4 100644 --- a/panda/src/display/graphicsStateGuardian.I +++ b/panda/src/display/graphicsStateGuardian.I @@ -62,6 +62,14 @@ release_all_index_buffers() { return _prepared_objects->release_all_index_buffers(); } +/** + * Frees the resources for all index buffers associated with this GSG. + */ +INLINE int GraphicsStateGuardian:: +release_all_shader_buffers() { + return _prepared_objects->release_all_shader_buffers(); +} + /** * Sets the active flag associated with the GraphicsStateGuardian. If the * GraphicsStateGuardian is marked inactive, nothing is rendered. This is not diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index fee041f57a..d5b2f769a0 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -62,12 +62,15 @@ #include #include -PStatCollector GraphicsStateGuardian::_vertex_buffer_switch_pcollector("Vertex buffer switch:Vertex"); -PStatCollector GraphicsStateGuardian::_index_buffer_switch_pcollector("Vertex buffer switch:Index"); +PStatCollector GraphicsStateGuardian::_vertex_buffer_switch_pcollector("Buffer switch:Vertex"); +PStatCollector GraphicsStateGuardian::_index_buffer_switch_pcollector("Buffer switch:Index"); +PStatCollector GraphicsStateGuardian::_shader_buffer_switch_pcollector("Buffer switch:Shader"); PStatCollector GraphicsStateGuardian::_load_vertex_buffer_pcollector("Draw:Transfer data:Vertex buffer"); PStatCollector GraphicsStateGuardian::_load_index_buffer_pcollector("Draw:Transfer data:Index buffer"); +PStatCollector GraphicsStateGuardian::_load_shader_buffer_pcollector("Draw:Transfer data:Shader buffer"); PStatCollector GraphicsStateGuardian::_create_vertex_buffer_pcollector("Draw:Transfer data:Create Vertex buffer"); PStatCollector GraphicsStateGuardian::_create_index_buffer_pcollector("Draw:Transfer data:Create Index buffer"); +PStatCollector GraphicsStateGuardian::_create_shader_buffer_pcollector("Draw:Transfer data:Create Shader buffer"); PStatCollector GraphicsStateGuardian::_load_texture_pcollector("Draw:Transfer data:Texture"); PStatCollector GraphicsStateGuardian::_data_transferred_pcollector("Data transferred"); PStatCollector GraphicsStateGuardian::_texmgrmem_total_pcollector("Texture manager"); @@ -104,6 +107,7 @@ PStatCollector GraphicsStateGuardian::_prepare_geom_pcollector("Draw:Prepare:Geo PStatCollector GraphicsStateGuardian::_prepare_shader_pcollector("Draw:Prepare:Shader"); PStatCollector GraphicsStateGuardian::_prepare_vertex_buffer_pcollector("Draw:Prepare:Vertex buffer"); PStatCollector GraphicsStateGuardian::_prepare_index_buffer_pcollector("Draw:Prepare:Index buffer"); +PStatCollector GraphicsStateGuardian::_prepare_shader_buffer_pcollector("Draw:Prepare:Shader buffer"); PStatCollector GraphicsStateGuardian::_draw_set_state_transform_pcollector("Draw:Set State:Transform"); PStatCollector GraphicsStateGuardian::_draw_set_state_alpha_test_pcollector("Draw:Set State:Alpha test"); @@ -657,6 +661,22 @@ void GraphicsStateGuardian:: release_index_buffer(IndexBufferContext *) { } +/** + * Prepares the indicated buffer for retained-mode rendering. + */ +BufferContext *GraphicsStateGuardian:: +prepare_shader_buffer(ShaderBuffer *) { + return (BufferContext *)NULL; +} + +/** + * Frees the resources previously allocated via a call to prepare_data(), + * including deleting the BufferContext itself, if necessary. + */ +void GraphicsStateGuardian:: +release_shader_buffer(BufferContext *) { +} + /** * 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 f8c9dabe6b..81643a1f11 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -88,6 +88,7 @@ PUBLISHED: INLINE int release_all_geoms(); INLINE int release_all_vertex_buffers(); INLINE int release_all_index_buffers(); + INLINE int release_all_shader_buffers(); INLINE void set_active(bool active); INLINE bool is_active() const; @@ -307,6 +308,9 @@ public: virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data); virtual void release_index_buffer(IndexBufferContext *ibc); + virtual BufferContext *prepare_shader_buffer(ShaderBuffer *data); + virtual void release_shader_buffer(BufferContext *ibc); + virtual void begin_occlusion_query(); virtual PT(OcclusionQueryContext) end_occlusion_query(); @@ -640,10 +644,13 @@ public: // Statistics static PStatCollector _vertex_buffer_switch_pcollector; static PStatCollector _index_buffer_switch_pcollector; + static PStatCollector _shader_buffer_switch_pcollector; static PStatCollector _load_vertex_buffer_pcollector; static PStatCollector _load_index_buffer_pcollector; + static PStatCollector _load_shader_buffer_pcollector; static PStatCollector _create_vertex_buffer_pcollector; static PStatCollector _create_index_buffer_pcollector; + static PStatCollector _create_shader_buffer_pcollector; static PStatCollector _load_texture_pcollector; static PStatCollector _data_transferred_pcollector; static PStatCollector _texmgrmem_total_pcollector; @@ -680,6 +687,7 @@ public: static PStatCollector _prepare_shader_pcollector; static PStatCollector _prepare_vertex_buffer_pcollector; static PStatCollector _prepare_index_buffer_pcollector; + static PStatCollector _prepare_shader_buffer_pcollector; // A whole slew of collectors to measure the cost of individual state // changes. These are disabled by default. diff --git a/panda/src/glstuff/glBufferContext_src.I b/panda/src/glstuff/glBufferContext_src.I new file mode 100644 index 0000000000..bec8283a18 --- /dev/null +++ b/panda/src/glstuff/glBufferContext_src.I @@ -0,0 +1,25 @@ +/** + * 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 glBufferContext_src.I + * @author rdb + * @date 2016-12-12 + */ + +/** + * + */ +INLINE CLP(BufferContext):: +CLP(BufferContext)(CLP(GraphicsStateGuardian) *glgsg, + PreparedGraphicsObjects *pgo) : + BufferContext(&pgo->_sbuffer_residency), + AdaptiveLruPage(0), + _glgsg(glgsg) +{ + _index = 0; +} diff --git a/panda/src/glstuff/glBufferContext_src.cxx b/panda/src/glstuff/glBufferContext_src.cxx new file mode 100644 index 0000000000..f2e588f945 --- /dev/null +++ b/panda/src/glstuff/glBufferContext_src.cxx @@ -0,0 +1,49 @@ +/** + * 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 glBufferContext_src.cxx + * @author rdb + * @date 2016-12-12 + */ + +TypeHandle CLP(BufferContext)::_type_handle; + +/** + * Evicts the page from the LRU. Called internally when the LRU determines + * that it is full. May also be called externally when necessary to + * explicitly evict the page. + * + * It is legal for this method to either evict the page as requested, do + * nothing (in which case the eviction will be requested again at the next + * epoch), or requeue itself on the tail of the queue (in which case the + * eviction will be requested again much later). + */ +void CLP(BufferContext):: +evict_lru() { + dequeue_lru(); + + // Make sure the buffer is unbound before we delete it. + if (_glgsg->_current_ibuffer_index == _index) { + if (GLCAT.is_debug() && gl_debug_buffers) { + GLCAT.debug() + << "unbinding index buffer\n"; + } + _glgsg->_glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + _glgsg->_current_ibuffer_index = 0; + } + + // Free the buffer. + _glgsg->_glDeleteBuffers(1, &_index); + + // We still need a valid index number, though, in case we want to re-load + // the buffer later. + _glgsg->_glGenBuffers(1, &_index); + + update_data_size_bytes(0); + set_resident(false); +} diff --git a/panda/src/glstuff/glBufferContext_src.h b/panda/src/glstuff/glBufferContext_src.h new file mode 100644 index 0000000000..b6c46aab76 --- /dev/null +++ b/panda/src/glstuff/glBufferContext_src.h @@ -0,0 +1,52 @@ +/** + * 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 glBufferContext_src.h + * @author rdb + * @date 2016-12-12 + */ + +#include "pandabase.h" +#include "bufferContext.h" +#include "deletedChain.h" + +/** + * Caches a GeomPrimitive on the GL as a buffer object. + */ +class EXPCL_GL CLP(BufferContext) : public BufferContext, public AdaptiveLruPage { +public: + INLINE CLP(BufferContext)(CLP(GraphicsStateGuardian) *glgsg, + PreparedGraphicsObjects *pgo); + ALLOC_DELETED_CHAIN(CLP(BufferContext)); + + virtual void evict_lru(); + + CLP(GraphicsStateGuardian) *_glgsg; + + // This is the GL "name" of the data object. + GLuint _index; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + BufferContext::init_type(); + register_type(_type_handle, CLASSPREFIX_QUOTED "BufferContext", + BufferContext::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 "glBufferContext_src.I" diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 80719c2e69..98c21610c5 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -1796,7 +1796,7 @@ reset() { } #endif -#ifndef OPENGLES +#ifndef OPENGLES_1 // Check for uniform buffers. #ifdef OPENGLES if (is_at_least_gl_version(3, 1) || has_extension("GL_ARB_uniform_buffer_object")) { @@ -1810,12 +1810,30 @@ reset() { get_extension_func("glGetActiveUniformBlockiv"); _glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) get_extension_func("glGetActiveUniformBlockName"); - - _glBindBufferBase = (PFNGLBINDBUFFERBASEPROC) - get_extension_func("glBindBufferBase"); } else { _supports_uniform_buffers = false; } + +#ifndef OPENGLES + // Check for SSBOs. + if (is_at_least_gl_version(4, 3) || has_extension("ARB_shader_storage_buffer_object")) { + _supports_shader_buffers = true; + _glGetProgramInterfaceiv = (PFNGLGETPROGRAMINTERFACEIVPROC) + get_extension_func("glGetProgramInterfaceiv"); + _glGetProgramResourceName = (PFNGLGETPROGRAMRESOURCENAMEPROC) + get_extension_func("glGetProgramResourceName"); + _glGetProgramResourceiv = (PFNGLGETPROGRAMRESOURCEIVPROC) + get_extension_func("glGetProgramResourceiv"); + } else +#endif + { + _supports_shader_buffers = false; + } + + if (_supports_uniform_buffers || _supports_shader_buffers) { + _glBindBufferBase = (PFNGLBINDBUFFERBASEPROC) + get_extension_func("glBindBufferBase"); + } #endif // Check whether we support geometry instancing and instanced vertex @@ -3031,6 +3049,9 @@ reset() { _current_vertex_buffers.clear(); _current_vertex_format.clear(); memset(_vertex_attrib_columns, 0, sizeof(const GeomVertexColumn *) * 32); + + _current_sbuffer_index = 0; + _current_sbuffer_base.clear(); #endif report_my_gl_errors(); @@ -5873,6 +5894,122 @@ setup_primitive(const unsigned char *&client_pointer, return true; } +#ifndef OPENGLES +/** + * Creates a new retained-mode representation of the given data, and returns a + * newly-allocated BufferContext pointer to reference it. It is the + * responsibility of the calling function to later call release_shader_buffer() + * with this same pointer (which will also delete the pointer). + * + * This function should not be called directly to prepare a buffer. Instead, + * call ShaderBuffer::prepare(). + */ +BufferContext *CLP(GraphicsStateGuardian):: +prepare_shader_buffer(ShaderBuffer *data) { + if (_supports_shader_buffers) { + PStatGPUTimer timer(this, _prepare_shader_buffer_pcollector); + + CLP(BufferContext) *gbc = new CLP(BufferContext)(this, _prepared_objects); + _glGenBuffers(1, &gbc->_index); + + if (GLCAT.is_debug() && gl_debug_buffers) { + GLCAT.debug() + << "creating shader buffer " << (int)gbc->_index << ": "<< *data << "\n"; + } + _glBindBuffer(GL_SHADER_STORAGE_BUFFER, gbc->_index); + _current_sbuffer_index = gbc->_index; + + if (_use_object_labels) { + string name = data->get_name(); + _glObjectLabel(GL_SHADER_STORAGE_BUFFER, gbc->_index, name.size(), name.data()); + } + + uint64_t num_bytes = data->get_data_size_bytes(); + if (_supports_buffer_storage) { + _glBufferStorage(GL_SHADER_STORAGE_BUFFER, num_bytes, data->get_initial_data(), 0); + } else { + _glBufferData(GL_SHADER_STORAGE_BUFFER, num_bytes, data->get_initial_data(), get_usage(data->get_usage_hint())); + } + + gbc->enqueue_lru(&_prepared_objects->_graphics_memory_lru); + + report_my_gl_errors(); + return gbc; + } + + return NULL; +} + +/** + * Binds the given shader buffer to the given binding slot. + */ +void CLP(GraphicsStateGuardian):: +apply_shader_buffer(GLuint base, ShaderBuffer *buffer) { + GLuint index = 0; + if (buffer != NULL) { + BufferContext *bc = buffer->prepare_now(get_prepared_objects(), this); + if (bc != NULL) { + CLP(BufferContext) *gbc = DCAST(CLP(BufferContext), bc); + index = gbc->_index; + gbc->set_active(true); + } + } + + if (base >= _current_sbuffer_base.size()) { + _current_sbuffer_base.resize(base + 1, 0); + } + + if (_current_sbuffer_base[base] != index) { + if (GLCAT.is_spam() && gl_debug_buffers) { + GLCAT.spam() + << "binding shader buffer " << (int)index + << " to index " << base << "\n"; + } + _glBindBufferBase(GL_SHADER_STORAGE_BUFFER, base, index); + _current_sbuffer_base[base] = index; + _current_sbuffer_index = index; + + report_my_gl_errors(); + } +} + +/** + * Frees the GL resources previously allocated for the data. This function + * should never be called directly; instead, call Data::release() (or simply + * let the Data destruct). + */ +void CLP(GraphicsStateGuardian):: +release_shader_buffer(BufferContext *bc) { + nassertv(_supports_buffers); + + CLP(BufferContext) *gbc = DCAST(CLP(BufferContext), bc); + + if (GLCAT.is_debug() && gl_debug_buffers) { + GLCAT.debug() + << "deleting shader buffer " << (int)gbc->_index << "\n"; + } + + // Make sure the buffer is unbound before we delete it. Not strictly + // necessary according to the OpenGL spec, but it might help out a flaky + // driver, and we need to keep our internal state consistent anyway. + if (_current_sbuffer_index == gbc->_index) { + if (GLCAT.is_spam() && gl_debug_buffers) { + GLCAT.spam() + << "unbinding shader buffer\n"; + } + _glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + _current_sbuffer_index = 0; + } + + _glDeleteBuffers(1, &gbc->_index); + report_my_gl_errors(); + + gbc->_index = 0; + + delete gbc; +} +#endif + #ifndef OPENGLES /** * Begins a new occlusion query. After this call, you may call @@ -6499,7 +6636,7 @@ do_issue_shade_model() { #ifndef OPENGLES_1 /** - * + * Called when the current ShaderAttrib state has changed. */ void CLP(GraphicsStateGuardian):: do_issue_shader() { @@ -6512,21 +6649,30 @@ do_issue_shader() { shader = _default_shader; nassertv(shader != NULL); } - #endif + if (shader) { - context = shader->prepare_now(get_prepared_objects(), this); + if (_current_shader != shader) { + context = shader->prepare_now(get_prepared_objects(), this); + } else { + context = _current_shader_context; + } } + #ifndef SUPPORT_FIXED_FUNCTION // If it failed, try applying the default shader. if (shader != _default_shader && (context == 0 || !context->valid())) { shader = _default_shader; nassertv(shader != NULL); - context = shader->prepare_now(get_prepared_objects(), this); + if (_current_shader != shader) { + context = shader->prepare_now(get_prepared_objects(), this); + } else { + context = _current_shader_context; + } } #endif - if (context == 0 || (context->valid() == false)) { + if (context == 0 || !context->valid()) { if (_current_shader_context != 0) { _current_shader_context->unbind(); _current_shader = 0; @@ -6538,12 +6684,16 @@ do_issue_shader() { // bind the new one. if (_current_shader_context != NULL && _current_shader->get_language() != shader->get_language()) { + // If it's a different type of shader, make sure to unbind the old. _current_shader_context->unbind(); } context->bind(); _current_shader = shader; - _current_shader_context = context; } + + // Bind the shader storage buffers. + context->update_shader_buffer_bindings(_current_shader_context); + _current_shader_context = context; } #ifndef OPENGLES @@ -10116,6 +10266,7 @@ set_state_and_transform(const RenderState *target, } #endif + // Update all of the state that is bound to the shader program. if (_current_shader_context != NULL) { _current_shader_context->set_state_and_transform(target, transform, _projection_mat); } diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index 7659ee13e5..85290cd6f2 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -344,6 +344,12 @@ public: const GeomPrimitivePipelineReader *reader, bool force); +#ifndef OPENGLES + virtual BufferContext *prepare_shader_buffer(ShaderBuffer *data); + void apply_shader_buffer(GLuint base, ShaderBuffer *buffer); + virtual void release_shader_buffer(BufferContext *bc); +#endif + #ifndef OPENGLES virtual void begin_occlusion_query(); virtual PT(OcclusionQueryContext) end_occlusion_query(); @@ -685,6 +691,9 @@ protected: bool _use_vertex_attrib_binding; CPT(GeomVertexFormat) _current_vertex_format; const GeomVertexColumn *_vertex_attrib_columns[32]; + + GLuint _current_sbuffer_index; + pvector _current_sbuffer_base; #endif int _active_texture_stage; @@ -811,6 +820,7 @@ public: #ifndef OPENGLES_1 bool _supports_uniform_buffers; + bool _supports_shader_buffers; PFNGLBINDBUFFERBASEPROC _glBindBufferBase; bool _supports_buffer_storage; @@ -995,6 +1005,9 @@ public: PFNGLMAKETEXTUREHANDLENONRESIDENTPROC _glMakeTextureHandleNonResident; PFNGLUNIFORMHANDLEUI64PROC _glUniformHandleui64; PFNGLUNIFORMHANDLEUI64VPROC _glUniformHandleui64v; + PFNGLGETPROGRAMINTERFACEIVPROC _glGetProgramInterfaceiv; + PFNGLGETPROGRAMRESOURCENAMEPROC _glGetProgramResourceName; + PFNGLGETPROGRAMRESOURCEIVPROC _glGetProgramResourceiv; #endif // !OPENGLES GLenum _edge_clamp; @@ -1090,6 +1103,7 @@ private: friend class CLP(VertexBufferContext); friend class CLP(IndexBufferContext); + friend class CLP(BufferContext); friend class CLP(ShaderContext); friend class CLP(CgShaderContext); friend class CLP(GraphicsBuffer); diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index 528b98804c..6947a39235 100644 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -324,6 +324,34 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext } } +#ifndef OPENGLES + // Get the used shader storage blocks. + if (_glgsg->_supports_shader_buffers) { + GLint block_count = 0, block_maxlength = 0; + + _glgsg->_glGetProgramInterfaceiv(_glsl_program, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &block_count); + _glgsg->_glGetProgramInterfaceiv(_glsl_program, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &block_maxlength); + + block_maxlength = max(64, block_maxlength); + char *block_name_cstr = (char *)alloca(block_maxlength); + + for (int i = 0; i < block_count; ++i) { + block_name_cstr[0] = 0; + _glgsg->_glGetProgramResourceName(_glsl_program, GL_SHADER_STORAGE_BLOCK, i, block_maxlength, NULL, block_name_cstr); + + const GLenum props[] = {GL_BUFFER_BINDING, GL_BUFFER_DATA_SIZE}; + GLint values[2]; + _glgsg->_glGetProgramResourceiv(_glsl_program, GL_SHADER_STORAGE_BLOCK, i, 2, props, 2, NULL, values); + + StorageBlock block; + block._name = InternalName::make(block_name_cstr); + block._binding_index = values[0]; + block._min_size = values[1]; + _storage_blocks.push_back(block); + } + } +#endif + // Bind the program, so that we can call glUniform1i for the textures. _glgsg->_glUseProgram(_glsl_program); @@ -2659,6 +2687,31 @@ update_shader_texture_bindings(ShaderContext *prev) { _glgsg->report_my_gl_errors(); } +/** + * Updates the shader buffer bindings for this shader. + */ +void CLP(ShaderContext):: +update_shader_buffer_bindings(ShaderContext *prev) { +#ifndef OPENGLES + // Update the shader storage buffer bindings. + const ShaderAttrib *attrib = _glgsg->_target_shader; + + for (size_t i = 0; i < _storage_blocks.size(); ++i) { + StorageBlock &block = _storage_blocks[i]; + + ShaderBuffer *buffer = attrib->get_shader_input_buffer(block._name); +#ifndef NDEBUG + if (buffer->get_data_size_bytes() < block._min_size) { + GLCAT.error() + << "cannot bind " << *buffer << " to shader because it is too small" + " (expected at least " << block._min_size << " bytes)\n"; + } +#endif + _glgsg->apply_shader_buffer(block._binding_index, buffer); + } +#endif +} + /** * This subroutine prints the infolog for a shader. */ diff --git a/panda/src/glstuff/glShaderContext_src.h b/panda/src/glstuff/glShaderContext_src.h index 183f068277..e143774ed6 100644 --- a/panda/src/glstuff/glShaderContext_src.h +++ b/panda/src/glstuff/glShaderContext_src.h @@ -55,6 +55,7 @@ public: bool update_shader_vertex_arrays(ShaderContext *prev, bool force); void disable_shader_texture_bindings() OVERRIDE; void update_shader_texture_bindings(ShaderContext *prev) OVERRIDE; + void update_shader_buffer_bindings(ShaderContext *prev) OVERRIDE; INLINE bool uses_standard_vertex_arrays(void); INLINE bool uses_custom_vertex_arrays(void); @@ -87,6 +88,17 @@ private: pmap _glsl_uniform_handles; #endif +#ifndef OPENGLES + struct StorageBlock { + CPT(InternalName) _name; + GLuint _binding_index; + GLint _min_size; + }; + typedef pvector StorageBlocks; + StorageBlocks _storage_blocks; + BitArray _used_storage_bindings; +#endif + struct ImageInput { CPT(InternalName) _name; CLP(TextureContext) *_gtc; diff --git a/panda/src/glstuff/glmisc_src.cxx b/panda/src/glstuff/glmisc_src.cxx index 544a5c2c79..fd2bcf7aee 100644 --- a/panda/src/glstuff/glmisc_src.cxx +++ b/panda/src/glstuff/glmisc_src.cxx @@ -331,6 +331,7 @@ void CLP(init_classes)() { CLP(SamplerContext)::init_type(); #endif CLP(VertexBufferContext)::init_type(); + CLP(BufferContext)::init_type(); CLP(GraphicsBuffer)::init_type(); #ifndef OPENGLES diff --git a/panda/src/glstuff/glstuff_src.cxx b/panda/src/glstuff/glstuff_src.cxx index af8bcc1df3..06abef89f4 100644 --- a/panda/src/glstuff/glstuff_src.cxx +++ b/panda/src/glstuff/glstuff_src.cxx @@ -21,6 +21,7 @@ #include "glSamplerContext_src.cxx" #include "glVertexBufferContext_src.cxx" #include "glIndexBufferContext_src.cxx" +#include "glBufferContext_src.cxx" #include "glOcclusionQueryContext_src.cxx" #include "glTimerQueryContext_src.cxx" #include "glLatencyQueryContext_src.cxx" diff --git a/panda/src/glstuff/glstuff_src.h b/panda/src/glstuff/glstuff_src.h index 46edb3ac83..02e3208445 100644 --- a/panda/src/glstuff/glstuff_src.h +++ b/panda/src/glstuff/glstuff_src.h @@ -33,6 +33,7 @@ #include "glSamplerContext_src.h" #include "glVertexBufferContext_src.h" #include "glIndexBufferContext_src.h" +#include "glBufferContext_src.h" #include "glOcclusionQueryContext_src.h" #include "glTimerQueryContext_src.h" #include "glLatencyQueryContext_src.h" diff --git a/panda/src/gobj/p3gobj_composite2.cxx b/panda/src/gobj/p3gobj_composite2.cxx index 81e23e5c07..56704b12b3 100644 --- a/panda/src/gobj/p3gobj_composite2.cxx +++ b/panda/src/gobj/p3gobj_composite2.cxx @@ -5,6 +5,7 @@ #include "samplerContext.cxx" #include "samplerState.cxx" #include "savedContext.cxx" +#include "shaderBuffer.cxx" #include "shaderContext.cxx" #include "shader.cxx" #include "simpleAllocator.cxx" diff --git a/panda/src/gobj/preparedGraphicsObjects.I b/panda/src/gobj/preparedGraphicsObjects.I index a9bd618ab5..6cc6daf914 100644 --- a/panda/src/gobj/preparedGraphicsObjects.I +++ b/panda/src/gobj/preparedGraphicsObjects.I @@ -45,6 +45,7 @@ release_all() { _texture_residency.set_levels(); _vbuffer_residency.set_levels(); _ibuffer_residency.set_levels(); + _sbuffer_residency.set_levels(); } /** @@ -58,7 +59,8 @@ get_num_queued() const { get_num_queued_geoms() + get_num_queued_shaders() + get_num_queued_vertex_buffers() + - get_num_queued_index_buffers()); + get_num_queued_index_buffers() + + get_num_queued_shader_buffers()); } /** @@ -72,7 +74,8 @@ get_num_prepared() const { get_num_prepared_geoms() + get_num_prepared_shaders() + get_num_prepared_vertex_buffers() + - get_num_prepared_index_buffers()); + get_num_prepared_index_buffers() + + get_num_prepared_shader_buffers()); } /** diff --git a/panda/src/gobj/preparedGraphicsObjects.cxx b/panda/src/gobj/preparedGraphicsObjects.cxx index 7b2b5e08a9..5c9ec855e3 100644 --- a/panda/src/gobj/preparedGraphicsObjects.cxx +++ b/panda/src/gobj/preparedGraphicsObjects.cxx @@ -41,6 +41,7 @@ PreparedGraphicsObjects() : _texture_residency(_name, "texture"), _vbuffer_residency(_name, "vbuffer"), _ibuffer_residency(_name, "ibuffer"), + _sbuffer_residency(_name, "sbuffer"), _graphics_memory_lru("graphics_memory_lru", graphics_memory_limit), _sampler_object_lru("sampler_object_lru", sampler_object_limit) { @@ -121,6 +122,16 @@ PreparedGraphicsObjects:: delete ibc; } _released_index_buffers.clear(); + + release_all_shader_buffers(); + Buffers::iterator bci; + for (bci = _released_shader_buffers.begin(); + bci != _released_shader_buffers.end(); + ++bci) { + BufferContext *bc = (BufferContext *)(*bci); + delete bc; + } + _released_shader_buffers.clear(); } /** @@ -167,6 +178,9 @@ show_residency_trackers(ostream &out) const { out << "\nIndex buffers:\n"; _ibuffer_residency.write(out, 2); + + out << "\nShader buffers:\n"; + _sbuffer_residency.write(out, 2); } /** @@ -1195,6 +1209,155 @@ prepare_index_buffer_now(GeomPrimitive *data, GraphicsStateGuardianBase *gsg) { return ibc; } +/** + * Indicates that a buffer would like to be put on the list to be prepared + * when the GSG is next ready to do this (presumably at the next frame). + */ +void PreparedGraphicsObjects:: +enqueue_shader_buffer(ShaderBuffer *data) { + ReMutexHolder holder(_lock); + + _enqueued_shader_buffers.insert(data); +} + +/** + * Returns true if the index buffer has been queued on this GSG, false + * otherwise. + */ +bool PreparedGraphicsObjects:: +is_shader_buffer_queued(const ShaderBuffer *data) const { + ReMutexHolder holder(_lock); + + EnqueuedShaderBuffers::const_iterator qi = _enqueued_shader_buffers.find((ShaderBuffer *)data); + return (qi != _enqueued_shader_buffers.end()); +} + +/** + * Removes a buffer from the queued list of data arrays to be prepared. + * Normally it is not necessary to call this, unless you change your mind + * about preparing it at the last minute, since the data will automatically be + * dequeued and prepared at the next frame. + * + * The return value is true if the buffer is successfully dequeued, false if + * it had not been queued. + */ +bool PreparedGraphicsObjects:: +dequeue_shader_buffer(ShaderBuffer *data) { + ReMutexHolder holder(_lock); + + EnqueuedShaderBuffers::iterator qi = _enqueued_shader_buffers.find(data); + if (qi != _enqueued_shader_buffers.end()) { + _enqueued_shader_buffers.erase(qi); + return true; + } + return false; +} + +/** + * Returns true if the index buffer has been prepared on this GSG, false + * otherwise. + */ +bool PreparedGraphicsObjects:: +is_shader_buffer_prepared(const ShaderBuffer *data) const { + return data->is_prepared((PreparedGraphicsObjects *)this); +} + +/** + * Indicates that a data context, created by a previous call to + * prepare_shader_buffer(), is no longer needed. The driver resources will not + * be freed until some GSG calls update(), indicating it is at a stage where + * it is ready to release datas--this prevents conflicts from threading or + * multiple GSG's sharing datas (we have no way of knowing which graphics + * context is currently active, or what state it's in, at the time + * release_shader_buffer is called). + */ +void PreparedGraphicsObjects:: +release_shader_buffer(BufferContext *bc) { + ReMutexHolder holder(_lock); + + bool removed = (_prepared_shader_buffers.erase(bc) != 0); + nassertv(removed); + + _released_shader_buffers.insert(bc); +} + +/** + * Releases all datas at once. This will force them to be reloaded into data + * memory for all GSG's that share this object. Returns the number of datas + * released. + */ +int PreparedGraphicsObjects:: +release_all_shader_buffers() { + ReMutexHolder holder(_lock); + + int num_shader_buffers = (int)_prepared_shader_buffers.size() + (int)_enqueued_shader_buffers.size(); + + Buffers::iterator bci; + for (bci = _prepared_shader_buffers.begin(); + bci != _prepared_shader_buffers.end(); + ++bci) { + + BufferContext *bc = (BufferContext *)(*bci); + _released_shader_buffers.insert(bc); + } + + _prepared_shader_buffers.clear(); + _enqueued_shader_buffers.clear(); + + return num_shader_buffers; +} + +/** + * Returns the number of index buffers that have been enqueued to be prepared + * on this GSG. + */ +int PreparedGraphicsObjects:: +get_num_queued_shader_buffers() const { + return _enqueued_shader_buffers.size(); +} + +/** + * Returns the number of index buffers that have already been prepared on this + * GSG. + */ +int PreparedGraphicsObjects:: +get_num_prepared_shader_buffers() const { + return _prepared_shader_buffers.size(); +} + +/** + * Immediately creates a new BufferContext for the indicated data and + * returns it. This assumes that the GraphicsStateGuardian is the currently + * active rendering context and that it is ready to accept new datas. If this + * is not necessarily the case, you should use enqueue_shader_buffer() instead. + * + * Normally, this function is not called directly. Call Data::prepare_now() + * instead. + * + * The BufferContext contains all of the pertinent information needed by + * the GSG to keep track of this one particular data, and will exist as long + * as the data is ready to be rendered. + * + * When either the Data or the PreparedGraphicsObjects object destructs, the + * BufferContext will be deleted. + */ +BufferContext *PreparedGraphicsObjects:: +prepare_shader_buffer_now(ShaderBuffer *data, GraphicsStateGuardianBase *gsg) { + ReMutexHolder holder(_lock); + + // Ask the GSG to create a brand new BufferContext. There might be + // several GSG's sharing the same set of datas; if so, it doesn't matter + // which of them creates the context (since they're all shared anyway). + BufferContext *bc = gsg->prepare_shader_buffer(data); + + if (bc != (BufferContext *)NULL) { + bool prepared = _prepared_shader_buffers.insert(bc).second; + nassertr(prepared, bc); + } + + return bc; +} + /** * This is called by the GraphicsStateGuardian to indicate that it is about to * begin processing of the frame. @@ -1276,6 +1439,7 @@ begin_frame(GraphicsStateGuardianBase *gsg, Thread *current_thread) { _texture_residency.begin_frame(current_thread); _vbuffer_residency.begin_frame(current_thread); _ibuffer_residency.begin_frame(current_thread); + _sbuffer_residency.begin_frame(current_thread); // Now prepare all the textures, geoms, and buffers awaiting preparation. EnqueuedTextures::iterator qti; @@ -1359,6 +1523,7 @@ end_frame(Thread *current_thread) { _texture_residency.end_frame(current_thread); _vbuffer_residency.end_frame(current_thread); _ibuffer_residency.end_frame(current_thread); + _sbuffer_residency.end_frame(current_thread); } /** diff --git a/panda/src/gobj/preparedGraphicsObjects.h b/panda/src/gobj/preparedGraphicsObjects.h index cc138576de..1dbde2d305 100644 --- a/panda/src/gobj/preparedGraphicsObjects.h +++ b/panda/src/gobj/preparedGraphicsObjects.h @@ -22,6 +22,7 @@ #include "geomVertexArrayData.h" #include "geomPrimitive.h" #include "shader.h" +#include "shaderBuffer.h" #include "pointerTo.h" #include "pStatCollector.h" #include "pset.h" @@ -35,6 +36,7 @@ class GeomContext; class ShaderContext; class VertexBufferContext; class IndexBufferContext; +class BufferContext; class GraphicsStateGuardianBase; /** @@ -142,6 +144,19 @@ PUBLISHED: prepare_index_buffer_now(GeomPrimitive *data, GraphicsStateGuardianBase *gsg); + void enqueue_shader_buffer(ShaderBuffer *data); + bool is_shader_buffer_queued(const ShaderBuffer *data) const; + bool dequeue_shader_buffer(ShaderBuffer *data); + bool is_shader_buffer_prepared(const ShaderBuffer *data) const; + void release_shader_buffer(BufferContext *bc); + int release_all_shader_buffers(); + int get_num_queued_shader_buffers() const; + int get_num_prepared_shader_buffers() const; + + BufferContext * + prepare_shader_buffer_now(ShaderBuffer *data, + GraphicsStateGuardianBase *gsg); + public: void begin_frame(GraphicsStateGuardianBase *gsg, Thread *current_thread); @@ -160,6 +175,7 @@ private: typedef phash_set Buffers; typedef phash_set< PT(GeomVertexArrayData) > EnqueuedVertexBuffers; typedef phash_set< PT(GeomPrimitive) > EnqueuedIndexBuffers; + typedef phash_set< PT(ShaderBuffer) > EnqueuedShaderBuffers; // Sampler states are stored a little bit differently, as they are mapped by // value and can't store the list of prepared samplers. @@ -207,6 +223,8 @@ private: EnqueuedVertexBuffers _enqueued_vertex_buffers; Buffers _prepared_index_buffers, _released_index_buffers; EnqueuedIndexBuffers _enqueued_index_buffers; + Buffers _prepared_shader_buffers, _released_shader_buffers; + EnqueuedShaderBuffers _enqueued_shader_buffers; BufferCache _vertex_buffer_cache; BufferCacheLRU _vertex_buffer_cache_lru; @@ -220,6 +238,7 @@ public: BufferResidencyTracker _texture_residency; BufferResidencyTracker _vbuffer_residency; BufferResidencyTracker _ibuffer_residency; + BufferResidencyTracker _sbuffer_residency; AdaptiveLru _graphics_memory_lru; SimpleLru _sampler_object_lru; diff --git a/panda/src/gobj/shaderBuffer.I b/panda/src/gobj/shaderBuffer.I new file mode 100644 index 0000000000..eb51f42f4e --- /dev/null +++ b/panda/src/gobj/shaderBuffer.I @@ -0,0 +1,63 @@ +/** + * 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 shaderBuffer.I + * @author rdb + * @date 2016-12-12 + */ + +/** + * Creates an uninitialized buffer object with the given size. For now, these + * parameters cannot be modified, but this may change in the future. + */ +INLINE ShaderBuffer:: +ShaderBuffer(const string &name, uint64_t size, UsageHint usage_hint) : + Namable(name), + _data_size_bytes(size), + _usage_hint(usage_hint) { +} + +/** + * Creates a buffer object initialized with the given data. For now, these + * parameters cannot be modified, but this may change in the future. + */ +INLINE ShaderBuffer:: +ShaderBuffer(const string &name, pvector initial_data, UsageHint usage_hint) : + Namable(name), + _data_size_bytes(initial_data.size()), + _usage_hint(usage_hint), + _initial_data(initial_data) { +} + +/** + * Returns the buffer size in bytes. + */ +INLINE uint64_t ShaderBuffer:: +get_data_size_bytes() const { + return _data_size_bytes; +} + +/** + * Returns the buffer usage hint. + */ +INLINE GeomEnums::UsageHint ShaderBuffer:: +get_usage_hint() const { + return _usage_hint; +} + +/** + * Returns a pointer to the initial buffer data, or NULL if not specified. + */ +INLINE const unsigned char *ShaderBuffer:: +get_initial_data() const { + if (_initial_data.empty()) { + return NULL; + } else { + return &_initial_data[0]; + } +} diff --git a/panda/src/gobj/shaderBuffer.cxx b/panda/src/gobj/shaderBuffer.cxx new file mode 100644 index 0000000000..a7cd5f8150 --- /dev/null +++ b/panda/src/gobj/shaderBuffer.cxx @@ -0,0 +1,193 @@ +/** + * 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 shaderBuffer.cxx + * @author rdb + * @date 2016-12-12 + */ + +#include "shaderBuffer.h" +#include "preparedGraphicsObjects.h" + +TypeHandle ShaderBuffer::_type_handle; + +/** + * + */ +void ShaderBuffer:: +output(ostream &out) const { + out << "buffer " << get_name() << ", " << _data_size_bytes << "B, " << _usage_hint; +} + +/** + * 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 + * data is already loaded into the GSG if it is expected to be rendered soon. + * + * Use this function instead of prepare_now() to preload datas from a user + * interface standpoint. + */ +void ShaderBuffer:: +prepare(PreparedGraphicsObjects *prepared_objects) { + prepared_objects->enqueue_shader_buffer(this); +} + +/** + * Returns true if the data has already been prepared or enqueued for + * preparation on the indicated GSG, false otherwise. + */ +bool ShaderBuffer:: +is_prepared(PreparedGraphicsObjects *prepared_objects) const { + if (_contexts == (Contexts *)NULL) { + return false; + } + Contexts::const_iterator ci; + ci = _contexts->find(prepared_objects); + if (ci != _contexts->end()) { + return true; + } + return prepared_objects->is_shader_buffer_queued(this); +} + +/** + * Creates a context for the data on the particular GSG, if it does not + * already exist. Returns the new (or old) BufferContext. This assumes + * that the GraphicsStateGuardian is the currently active rendering context + * and that it is ready to accept new datas. If this is not necessarily the + * case, you should use prepare() instead. + * + * Normally, this is not called directly except by the GraphicsStateGuardian; + * a data does not need to be explicitly prepared by the user before it may be + * rendered. + */ +BufferContext *ShaderBuffer:: +prepare_now(PreparedGraphicsObjects *prepared_objects, + GraphicsStateGuardianBase *gsg) { + if (_contexts == (Contexts *)NULL) { + _contexts = new Contexts; + } + Contexts::const_iterator ci; + ci = _contexts->find(prepared_objects); + if (ci != _contexts->end()) { + return (*ci).second; + } + + BufferContext *vbc = prepared_objects->prepare_shader_buffer_now(this, gsg); + if (vbc != (BufferContext *)NULL) { + (*_contexts)[prepared_objects] = vbc; + } + return vbc; +} + +/** + * Frees the data context only on the indicated object, if it exists there. + * Returns true if it was released, false if it had not been prepared. + */ +bool ShaderBuffer:: +release(PreparedGraphicsObjects *prepared_objects) { + if (_contexts != (Contexts *)NULL) { + Contexts::iterator ci; + ci = _contexts->find(prepared_objects); + if (ci != _contexts->end()) { + BufferContext *vbc = (*ci).second; + prepared_objects->release_shader_buffer(vbc); + return true; + } + } + + // Maybe it wasn't prepared yet, but it's about to be. + return prepared_objects->dequeue_shader_buffer(this); +} + +/** + * Frees the context allocated on all objects for which the data has been + * declared. Returns the number of contexts which have been freed. + */ +int ShaderBuffer:: +release_all() { + int num_freed = 0; + + if (_contexts != (Contexts *)NULL) { + // We have to traverse a copy of the _contexts list, because the + // PreparedGraphicsObjects object will call clear_prepared() in response + // to each release_shader_buffer(), and we don't want to be modifying the + // _contexts list while we're traversing it. + Contexts temp = *_contexts; + num_freed = (int)_contexts->size(); + + Contexts::const_iterator ci; + for (ci = temp.begin(); ci != temp.end(); ++ci) { + PreparedGraphicsObjects *prepared_objects = (*ci).first; + BufferContext *vbc = (*ci).second; + prepared_objects->release_shader_buffer(vbc); + } + + // Now that we've called release_shader_buffer() on every known context, + // the _contexts list should have completely emptied itself. + nassertr(_contexts == NULL, num_freed); + } + + return num_freed; +} + +/** + * Tells the BamReader how to create objects of type ParamValue. + */ +void ShaderBuffer:: +register_with_read_factory() { + BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); +} + +/** + * Writes the contents of this object to the datagram for shipping out to a + * Bam file. + */ +void ShaderBuffer:: +write_datagram(BamWriter *manager, Datagram &dg) { + dg.add_string(get_name()); + dg.add_uint64(_data_size_bytes); + dg.add_uint8(_usage_hint); + dg.add_bool(!_initial_data.empty()); + dg.append_data(_initial_data.data(), _initial_data.size()); +} + +/** + * This function is called by the BamReader's factory when a new object of + * type ParamValue is encountered in the Bam file. It should create the + * ParamValue and extract its information from the file. + */ +TypedWritable *ShaderBuffer:: +make_from_bam(const FactoryParams ¶ms) { + ShaderBuffer *param = new ShaderBuffer; + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + param->fillin(scan, manager); + + return param; +} + +/** + * This internal function is called by make_from_bam to read in all of the + * relevant data from the BamFile for the new ParamValue. + */ +void ShaderBuffer:: +fillin(DatagramIterator &scan, BamReader *manager) { + set_name(scan.get_string()); + _data_size_bytes = scan.get_uint64(); + _usage_hint = (UsageHint)scan.get_uint8(); + + if (scan.get_bool() && _data_size_bytes > 0) { + nassertv_always(_data_size_bytes <= scan.get_remaining_size()); + _initial_data.resize(_data_size_bytes); + scan.extract_bytes(&_initial_data[0], _data_size_bytes); + } else { + _initial_data.clear(); + } +} diff --git a/panda/src/gobj/shaderBuffer.h b/panda/src/gobj/shaderBuffer.h new file mode 100644 index 0000000000..5faa4322ac --- /dev/null +++ b/panda/src/gobj/shaderBuffer.h @@ -0,0 +1,97 @@ +/** + * 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 shaderBuffer.h + * @author rdb + * @date 2016-12-12 + */ + +#ifndef SHADERBUFFER_H +#define SHADERBUFFER_H + +#include "pandabase.h" +#include "namable.h" +#include "geomEnums.h" + +class BufferContext; +class PreparedGraphicsObjects; + +/** + * This is a generic buffer object that lives in graphics memory. + */ +class EXPCL_PANDA_GOBJ ShaderBuffer : public TypedWritableReferenceCount, public Namable, public GeomEnums { +private: + INLINE ShaderBuffer() DEFAULT_CTOR; + +PUBLISHED: + INLINE ShaderBuffer(const string &name, uint64_t size, UsageHint usage_hint); + INLINE ShaderBuffer(const string &name, pvector initial_data, UsageHint usage_hint); + +public: + INLINE uint64_t get_data_size_bytes() const; + INLINE UsageHint get_usage_hint() const; + INLINE const unsigned char *get_initial_data() const; + + virtual void output(ostream &out) const; + +PUBLISHED: + MAKE_PROPERTY(data_size_bytes, get_data_size_bytes); + MAKE_PROPERTY(usage_hint, get_usage_hint); + + void prepare(PreparedGraphicsObjects *prepared_objects); + bool is_prepared(PreparedGraphicsObjects *prepared_objects) const; + + BufferContext *prepare_now(PreparedGraphicsObjects *prepared_objects, + GraphicsStateGuardianBase *gsg); + bool release(PreparedGraphicsObjects *prepared_objects); + int release_all(); + +private: + uint64_t _data_size_bytes; + UsageHint _usage_hint; + pvector _initial_data; + + typedef pmap Contexts; + Contexts *_contexts; + +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &dg); + +protected: + static TypedWritable *make_from_bam(const FactoryParams ¶ms); + void fillin(DatagramIterator &scan, BamReader *manager); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedWritableReferenceCount::init_type(); + Namable::init_type(); + register_type(_type_handle, "ShaderBuffer", + TypedWritableReferenceCount::get_class_type(), + Namable::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +INLINE ostream &operator << (ostream &out, const ShaderBuffer &m) { + m.output(out); + return out; +} + +#include "shaderBuffer.I" + +#endif diff --git a/panda/src/gobj/shaderContext.h b/panda/src/gobj/shaderContext.h index 2b356cef8f..7a5312faa2 100644 --- a/panda/src/gobj/shaderContext.h +++ b/panda/src/gobj/shaderContext.h @@ -42,6 +42,7 @@ public: INLINE virtual bool update_shader_vertex_arrays(ShaderContext *prev, bool force) { return false; }; INLINE virtual void disable_shader_texture_bindings() {}; INLINE virtual void update_shader_texture_bindings(ShaderContext *prev) {}; + INLINE virtual void update_shader_buffer_bindings(ShaderContext *prev) {}; INLINE virtual bool uses_standard_vertex_arrays(void) { return true; }; INLINE virtual bool uses_custom_vertex_arrays(void) { return false; }; diff --git a/panda/src/gsgbase/graphicsStateGuardianBase.h b/panda/src/gsgbase/graphicsStateGuardianBase.h index 90455869ac..c45f36aca0 100644 --- a/panda/src/gsgbase/graphicsStateGuardianBase.h +++ b/panda/src/gsgbase/graphicsStateGuardianBase.h @@ -31,6 +31,7 @@ class GraphicsOutputBase; class VertexBufferContext; class IndexBufferContext; +class BufferContext; class GeomContext; class GeomNode; class Geom; @@ -57,6 +58,7 @@ class SamplerContext; class SamplerState; class Shader; class ShaderContext; +class ShaderBuffer; class RenderState; class TransformState; class Material; @@ -162,6 +164,9 @@ public: virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data)=0; virtual void release_index_buffer(IndexBufferContext *ibc)=0; + virtual BufferContext *prepare_shader_buffer(ShaderBuffer *data)=0; + virtual void release_shader_buffer(BufferContext *ibc)=0; + virtual void dispatch_compute(int size_x, int size_y, int size_z)=0; virtual PT(GeomMunger) get_geom_munger(const RenderState *state, diff --git a/panda/src/pgraph/nodePath.I b/panda/src/pgraph/nodePath.I index 1e9813debc..cfcef09112 100644 --- a/panda/src/pgraph/nodePath.I +++ b/panda/src/pgraph/nodePath.I @@ -1253,6 +1253,14 @@ set_shader_input(CPT_InternalName id, Texture *tex, bool read, bool write, int z set_shader_input(new ShaderInput(id, tex, read, write, z, n, priority)); } +/** + * + */ +INLINE void NodePath:: +set_shader_input(CPT_InternalName id, ShaderBuffer *buf, int priority) { + set_shader_input(new ShaderInput(id, buf, priority)); +} + /** * */ diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index a30fc1d40d..1bbe0845cf 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -61,6 +61,7 @@ class GlobPattern; class PreparedGraphicsObjects; class SamplerState; class Shader; +class ShaderBuffer; class ShaderInput; // @@ -632,6 +633,7 @@ PUBLISHED: INLINE void set_shader_input(CPT_InternalName id, Texture *tex, int priority=0); INLINE void set_shader_input(CPT_InternalName id, Texture *tex, const SamplerState &sampler, int priority=0); INLINE void set_shader_input(CPT_InternalName id, Texture *tex, bool read, bool write, int z=-1, int n=0, int priority=0); + INLINE void set_shader_input(CPT_InternalName id, ShaderBuffer *buf, int priority=0); INLINE void set_shader_input(CPT_InternalName id, const NodePath &np, int priority=0); INLINE void set_shader_input(CPT_InternalName id, const PTA_float &v, int priority=0); INLINE void set_shader_input(CPT_InternalName id, const PTA_double &v, int priority=0); diff --git a/panda/src/pgraph/shaderAttrib.cxx b/panda/src/pgraph/shaderAttrib.cxx index 93ccf45bc2..758d980a50 100644 --- a/panda/src/pgraph/shaderAttrib.cxx +++ b/panda/src/pgraph/shaderAttrib.cxx @@ -25,6 +25,7 @@ #include "datagram.h" #include "datagramIterator.h" #include "nodePath.h" +#include "shaderBuffer.h" TypeHandle ShaderAttrib::_type_handle; int ShaderAttrib::_attrib_slot; @@ -461,6 +462,34 @@ get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const { } } +/** + * Returns the ShaderInput as a ShaderBuffer. Assertion fails if there is + * none, or if it is not a ShaderBuffer. + */ +ShaderBuffer *ShaderAttrib:: +get_shader_input_buffer(const InternalName *id) const { + Inputs::const_iterator i = _inputs.find(id); + if (i == _inputs.end()) { + ostringstream strm; + strm << "Shader input " << id->get_name() << " is not present.\n"; + nassert_raise(strm.str()); + return NULL; + } else { + const ShaderInput *p = (*i).second; + + if (p->get_value_type() == ShaderInput::M_buffer) { + ShaderBuffer *value; + DCAST_INTO_R(value, p->_value, NULL); + return value; + } + + ostringstream strm; + strm << "Shader input " << id->get_name() << " is not a ShaderBuffer.\n"; + nassert_raise(strm.str()); + return NULL; + } +} + /** * Returns the shader object associated with the node. If get_override * returns true, but get_shader returns NULL, that means that this attribute diff --git a/panda/src/pgraph/shaderAttrib.h b/panda/src/pgraph/shaderAttrib.h index 08676286f5..5a28e929b1 100644 --- a/panda/src/pgraph/shaderAttrib.h +++ b/panda/src/pgraph/shaderAttrib.h @@ -111,6 +111,7 @@ PUBLISHED: Texture *get_shader_input_texture(const InternalName *id, SamplerState *sampler=NULL) const; const Shader::ShaderPtrData *get_shader_input_ptr(const InternalName *id) const; const LMatrix4 &get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const; + ShaderBuffer *get_shader_input_buffer(const InternalName *id) const; static void register_with_read_factory(); diff --git a/panda/src/pgraph/shaderInput.I b/panda/src/pgraph/shaderInput.I index 52f46369ad..7344717e47 100644 --- a/panda/src/pgraph/shaderInput.I +++ b/panda/src/pgraph/shaderInput.I @@ -55,6 +55,18 @@ ShaderInput(CPT_InternalName name, ParamValueBase *param, int priority) : { } +/** + * + */ +INLINE ShaderInput:: +ShaderInput(CPT_InternalName name, ShaderBuffer *buf, int priority) : + _name(MOVE(name)), + _type(M_buffer), + _priority(priority), + _value(buf) +{ +} + /** * */ diff --git a/panda/src/pgraph/shaderInput.h b/panda/src/pgraph/shaderInput.h index 260d5a6ab1..c91c790618 100644 --- a/panda/src/pgraph/shaderInput.h +++ b/panda/src/pgraph/shaderInput.h @@ -31,6 +31,7 @@ #include "samplerState.h" #include "shader.h" #include "texture.h" +#include "shaderBuffer.h" /** * This is a small container class that can hold any one of the value types @@ -52,6 +53,7 @@ PUBLISHED: INLINE ShaderInput(CPT_InternalName name, int priority=0); INLINE ShaderInput(CPT_InternalName name, Texture *tex, int priority=0); INLINE ShaderInput(CPT_InternalName name, ParamValueBase *param, int priority=0); + INLINE ShaderInput(CPT_InternalName name, ShaderBuffer *buf, int priority=0); INLINE ShaderInput(CPT_InternalName name, const PTA_float &ptr, int priority=0); INLINE ShaderInput(CPT_InternalName name, const PTA_LVecBase4f &ptr, int priority=0); INLINE ShaderInput(CPT_InternalName name, const PTA_LVecBase3f &ptr, int priority=0); @@ -96,7 +98,8 @@ PUBLISHED: M_numeric, M_texture_sampler, M_param, - M_texture_image + M_texture_image, + M_buffer, }; INLINE const InternalName *get_name() const; @@ -123,6 +126,8 @@ private: int _priority; int _type; + friend class ShaderAttrib; + public: static TypeHandle get_class_type() { return _type_handle;