Implement support for SSBOs
This commit is contained in:
parent
23645cc407
commit
34068dc0c1
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -62,12 +62,15 @@
|
|||
#include <algorithm>
|
||||
#include <limits.h>
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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"
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<GLuint> _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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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<GLint, GLuint64> _glsl_uniform_handles;
|
||||
#endif
|
||||
|
||||
#ifndef OPENGLES
|
||||
struct StorageBlock {
|
||||
CPT(InternalName) _name;
|
||||
GLuint _binding_index;
|
||||
GLint _min_size;
|
||||
};
|
||||
typedef pvector<StorageBlock> StorageBlocks;
|
||||
StorageBlocks _storage_blocks;
|
||||
BitArray _used_storage_bindings;
|
||||
#endif
|
||||
|
||||
struct ImageInput {
|
||||
CPT(InternalName) _name;
|
||||
CLP(TextureContext) *_gtc;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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<BufferContext *, pointer_hash> 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;
|
||||
|
|
|
|||
|
|
@ -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<unsigned char> 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];
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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<unsigned char> 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<unsigned char> _initial_data;
|
||||
|
||||
typedef pmap<PreparedGraphicsObjects *, BufferContext *> 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
|
||||
|
|
@ -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; };
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue