|
@@ -1796,7 +1796,7 @@ reset() {
|
|
|
}
|
|
}
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
-#ifndef OPENGLES
|
|
|
|
|
|
|
+#ifndef OPENGLES_1
|
|
|
// Check for uniform buffers.
|
|
// Check for uniform buffers.
|
|
|
#ifdef OPENGLES
|
|
#ifdef OPENGLES
|
|
|
if (is_at_least_gl_version(3, 1) || has_extension("GL_ARB_uniform_buffer_object")) {
|
|
if (is_at_least_gl_version(3, 1) || has_extension("GL_ARB_uniform_buffer_object")) {
|
|
@@ -1810,11 +1810,29 @@ reset() {
|
|
|
get_extension_func("glGetActiveUniformBlockiv");
|
|
get_extension_func("glGetActiveUniformBlockiv");
|
|
|
_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)
|
|
_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)
|
|
|
get_extension_func("glGetActiveUniformBlockName");
|
|
get_extension_func("glGetActiveUniformBlockName");
|
|
|
|
|
+ } 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)
|
|
_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)
|
|
|
get_extension_func("glBindBufferBase");
|
|
get_extension_func("glBindBufferBase");
|
|
|
- } else {
|
|
|
|
|
- _supports_uniform_buffers = false;
|
|
|
|
|
}
|
|
}
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
@@ -3031,6 +3049,9 @@ reset() {
|
|
|
_current_vertex_buffers.clear();
|
|
_current_vertex_buffers.clear();
|
|
|
_current_vertex_format.clear();
|
|
_current_vertex_format.clear();
|
|
|
memset(_vertex_attrib_columns, 0, sizeof(const GeomVertexColumn *) * 32);
|
|
memset(_vertex_attrib_columns, 0, sizeof(const GeomVertexColumn *) * 32);
|
|
|
|
|
+
|
|
|
|
|
+ _current_sbuffer_index = 0;
|
|
|
|
|
+ _current_sbuffer_base.clear();
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
report_my_gl_errors();
|
|
report_my_gl_errors();
|
|
@@ -5873,6 +5894,122 @@ setup_primitive(const unsigned char *&client_pointer,
|
|
|
return true;
|
|
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
|
|
#ifndef OPENGLES
|
|
|
/**
|
|
/**
|
|
|
* Begins a new occlusion query. After this call, you may call
|
|
* Begins a new occlusion query. After this call, you may call
|
|
@@ -6499,7 +6636,7 @@ do_issue_shade_model() {
|
|
|
|
|
|
|
|
#ifndef OPENGLES_1
|
|
#ifndef OPENGLES_1
|
|
|
/**
|
|
/**
|
|
|
- *
|
|
|
|
|
|
|
+ * Called when the current ShaderAttrib state has changed.
|
|
|
*/
|
|
*/
|
|
|
void CLP(GraphicsStateGuardian)::
|
|
void CLP(GraphicsStateGuardian)::
|
|
|
do_issue_shader() {
|
|
do_issue_shader() {
|
|
@@ -6512,21 +6649,30 @@ do_issue_shader() {
|
|
|
shader = _default_shader;
|
|
shader = _default_shader;
|
|
|
nassertv(shader != NULL);
|
|
nassertv(shader != NULL);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
#endif
|
|
#endif
|
|
|
|
|
+
|
|
|
if (shader) {
|
|
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
|
|
#ifndef SUPPORT_FIXED_FUNCTION
|
|
|
// If it failed, try applying the default shader.
|
|
// If it failed, try applying the default shader.
|
|
|
if (shader != _default_shader && (context == 0 || !context->valid())) {
|
|
if (shader != _default_shader && (context == 0 || !context->valid())) {
|
|
|
shader = _default_shader;
|
|
shader = _default_shader;
|
|
|
nassertv(shader != NULL);
|
|
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
|
|
#endif
|
|
|
|
|
|
|
|
- if (context == 0 || (context->valid() == false)) {
|
|
|
|
|
|
|
+ if (context == 0 || !context->valid()) {
|
|
|
if (_current_shader_context != 0) {
|
|
if (_current_shader_context != 0) {
|
|
|
_current_shader_context->unbind();
|
|
_current_shader_context->unbind();
|
|
|
_current_shader = 0;
|
|
_current_shader = 0;
|
|
@@ -6538,12 +6684,16 @@ do_issue_shader() {
|
|
|
// bind the new one.
|
|
// bind the new one.
|
|
|
if (_current_shader_context != NULL &&
|
|
if (_current_shader_context != NULL &&
|
|
|
_current_shader->get_language() != shader->get_language()) {
|
|
_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();
|
|
_current_shader_context->unbind();
|
|
|
}
|
|
}
|
|
|
context->bind();
|
|
context->bind();
|
|
|
_current_shader = shader;
|
|
_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
|
|
#ifndef OPENGLES
|
|
@@ -10116,6 +10266,7 @@ set_state_and_transform(const RenderState *target,
|
|
|
}
|
|
}
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
|
|
+ // Update all of the state that is bound to the shader program.
|
|
|
if (_current_shader_context != NULL) {
|
|
if (_current_shader_context != NULL) {
|
|
|
_current_shader_context->set_state_and_transform(target, transform, _projection_mat);
|
|
_current_shader_context->set_state_and_transform(target, transform, _projection_mat);
|
|
|
}
|
|
}
|