|
|
@@ -33,6 +33,7 @@
|
|
|
#include "sparseArray.h"
|
|
|
#include "spirVTransformer.h"
|
|
|
#include "spirVInjectAlphaTestPass.h"
|
|
|
+#include "spirVEmulateTextureQueriesPass.h"
|
|
|
|
|
|
#define SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
|
|
|
#include <spirv_cross/spirv_glsl.hpp>
|
|
|
@@ -198,6 +199,10 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext
|
|
|
if (!valid) {
|
|
|
_shader->_error_flag = true;
|
|
|
}
|
|
|
+
|
|
|
+#ifdef DO_PSTATS
|
|
|
+ _compute_dispatch_pcollector = PStatCollector(_glgsg->_compute_dispatch_pcollector, s->get_debug_name());
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -393,9 +398,9 @@ compile_for(RenderAttrib::PandaCompareFunc alpha_test_mode) {
|
|
|
}
|
|
|
|
|
|
UniformCalls &calls = block._calls[alpha_test_mode];
|
|
|
- r_collect_uniforms(program, param, calls, param._type, name.c_str(), sym_buffer,
|
|
|
- actual_location, active_locations, resource_index,
|
|
|
- binding);
|
|
|
+ r_collect_uniforms(alpha_test_mode, param, calls, param._type, name.c_str(),
|
|
|
+ sym_buffer, actual_location, active_locations,
|
|
|
+ resource_index, binding);
|
|
|
|
|
|
if (block_index < 0 && (!calls._matrices.empty() || !calls._vectors.empty())) {
|
|
|
block._dep = param._binding->get_state_dep();
|
|
|
@@ -493,12 +498,14 @@ r_count_locations_bindings(const ShaderType *type,
|
|
|
* Also finds all resources and adds them to the respective arrays.
|
|
|
*/
|
|
|
void CLP(ShaderContext)::
|
|
|
-r_collect_uniforms(GLuint program,
|
|
|
+r_collect_uniforms(RenderAttrib::PandaCompareFunc alpha_test_mode,
|
|
|
const Shader::Parameter ¶m, UniformCalls &calls,
|
|
|
const ShaderType *type, const char *name, const char *sym,
|
|
|
int &cur_location, const SparseArray &active_locations,
|
|
|
int &resource_index, int &cur_binding, size_t offset) {
|
|
|
|
|
|
+ GLuint program = _linked_programs[alpha_test_mode];
|
|
|
+
|
|
|
ShaderType::ScalarType scalar_type;
|
|
|
uint32_t num_elements;
|
|
|
uint32_t num_rows;
|
|
|
@@ -620,7 +627,7 @@ r_collect_uniforms(GLuint program,
|
|
|
for (uint32_t i = 0; i < array_type->get_num_elements(); ++i) {
|
|
|
sprintf(name_buffer, "%s[%u]", name, i);
|
|
|
sprintf(sym_buffer, "%s[%u]", sym, i);
|
|
|
- r_collect_uniforms(program, param, calls, element_type, name_buffer, sym_buffer,
|
|
|
+ r_collect_uniforms(alpha_test_mode, param, calls, element_type, name_buffer, sym_buffer,
|
|
|
cur_location, active_locations, resource_index, cur_binding,
|
|
|
offset);
|
|
|
offset += stride;
|
|
|
@@ -638,7 +645,7 @@ r_collect_uniforms(GLuint program,
|
|
|
|
|
|
// We have named struct members m0, m1, etc. in declaration order.
|
|
|
sprintf(sym_buffer, "%s.m%u", sym, i);
|
|
|
- r_collect_uniforms(program, param, calls, member.type, qualname.c_str(), sym_buffer,
|
|
|
+ r_collect_uniforms(alpha_test_mode, param, calls, member.type, qualname.c_str(), sym_buffer,
|
|
|
cur_location, active_locations, resource_index, cur_binding,
|
|
|
offset + member.offset);
|
|
|
}
|
|
|
@@ -670,7 +677,7 @@ r_collect_uniforms(GLuint program,
|
|
|
|
|
|
StorageBlock block;
|
|
|
block._binding = param._binding;
|
|
|
- block._resource_id = param._binding->get_resource_id(resource_index++, type);
|
|
|
+ block._resource_id = param._binding->get_resource_id(resource_index++);
|
|
|
block._binding_index = binding;
|
|
|
_storage_blocks.push_back(std::move(block));
|
|
|
_storage_block_bindings |= (1 << binding);
|
|
|
@@ -681,15 +688,34 @@ r_collect_uniforms(GLuint program,
|
|
|
int location = cur_location;
|
|
|
if (location < 0) {
|
|
|
location = _glgsg->_glGetUniformLocation(program, _is_legacy ? name : sym);
|
|
|
- if (location < 0) {
|
|
|
- return;
|
|
|
- }
|
|
|
} else {
|
|
|
++cur_location;
|
|
|
if (!active_locations.get_bit(location)) {
|
|
|
- return;
|
|
|
+ location = -1;
|
|
|
}
|
|
|
}
|
|
|
+ int size_location = -1;
|
|
|
+ if (_emulated_caps & (Shader::C_image_query_size | Shader::C_texture_query_size | Shader::C_texture_query_levels)) {
|
|
|
+ // Do we have a separate size input?
|
|
|
+ size_t sym_len = strlen(sym);
|
|
|
+ char *size_name_buffer = (char *)alloca(sym_len + 3);
|
|
|
+ char *p = size_name_buffer;
|
|
|
+ for (size_t i = 0; i < sym_len; ++i) {
|
|
|
+ if (sym[i] == '[' || sym[i] == '.') {
|
|
|
+ *p++ = '_';
|
|
|
+ }
|
|
|
+ else if (sym[i] != 'm' && sym[i] != ']') {
|
|
|
+ *p++ = sym[i];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ *p++ = '_';
|
|
|
+ *p++ = 's';
|
|
|
+ *p = '\0';
|
|
|
+ size_location = _glgsg->_glGetUniformLocation(program, size_name_buffer);
|
|
|
+ }
|
|
|
+ if (location < 0 && size_location < 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
if (GLCAT.is_debug()) {
|
|
|
GLCAT.debug()
|
|
|
@@ -700,19 +726,30 @@ r_collect_uniforms(GLuint program,
|
|
|
if (const ShaderType::SampledImage *sampler = type->as_sampled_image()) {
|
|
|
TextureUnit unit;
|
|
|
unit._binding = param._binding;
|
|
|
- unit._resource_id = param._binding->get_resource_id(resource_index++, type);
|
|
|
+ unit._resource_id = param._binding->get_resource_id(resource_index++);
|
|
|
unit._target = _glgsg->get_texture_target(sampler->get_texture_type());
|
|
|
|
|
|
+ for (int i = 0; i < RenderAttrib::M_always; ++i) {
|
|
|
+ unit._size_loc[i] = -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (size_location >= 0) {
|
|
|
+ unit._size_loc[alpha_test_mode] = size_location;
|
|
|
+ }
|
|
|
+
|
|
|
// Check if we already have a unit with these properties. If so, we alias
|
|
|
// the binding. This will also prevent duplicating texture units when the
|
|
|
// shader is compiled multiple times, for different alpha test modes.
|
|
|
GLint binding = -1;
|
|
|
for (size_t i = 0; i < _texture_units.size(); ++i) {
|
|
|
- const TextureUnit &other_unit = _texture_units[i];
|
|
|
+ TextureUnit &other_unit = _texture_units[i];
|
|
|
if (other_unit._binding == unit._binding &&
|
|
|
other_unit._resource_id == unit._resource_id &&
|
|
|
other_unit._target == unit._target) {
|
|
|
binding = (GLint)i;
|
|
|
+ if (unit._size_loc[alpha_test_mode] >= 0) {
|
|
|
+ other_unit._size_loc[alpha_test_mode] = unit._size_loc[alpha_test_mode];
|
|
|
+ }
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
@@ -721,7 +758,9 @@ r_collect_uniforms(GLuint program,
|
|
|
binding = (GLint)_texture_units.size();
|
|
|
_texture_units.push_back(std::move(unit));
|
|
|
}
|
|
|
- _glgsg->_glUniform1i(location, binding);
|
|
|
+ if (location >= 0) {
|
|
|
+ _glgsg->_glUniform1i(location, binding);
|
|
|
+ }
|
|
|
}
|
|
|
else if (const ShaderType::Image *image = type->as_image()) {
|
|
|
// In OpenGL ES, we can't specify a binding index after the fact.
|
|
|
@@ -729,6 +768,12 @@ r_collect_uniforms(GLuint program,
|
|
|
// the driver (or the user) providing a unique one.
|
|
|
GLint binding = -1;
|
|
|
#ifdef OPENGLES
|
|
|
+ if (location < 0) {
|
|
|
+ // There's an edge case here if we use imageSize without any other
|
|
|
+ // accesses to the image, and the image itself is optimized out.
|
|
|
+ // I don't think it's very realistic, so I haven't bothered with it.
|
|
|
+ return;
|
|
|
+ }
|
|
|
glGetUniformiv(program, location, &binding);
|
|
|
if (binding < 0) {
|
|
|
return;
|
|
|
@@ -741,18 +786,29 @@ r_collect_uniforms(GLuint program,
|
|
|
ImageUnit unit;
|
|
|
#endif
|
|
|
unit._binding = param._binding;
|
|
|
- unit._resource_id = param._binding->get_resource_id(resource_index++, type);
|
|
|
+ unit._resource_id = param._binding->get_resource_id(resource_index++);
|
|
|
unit._access = image->get_access();
|
|
|
unit._written = false;
|
|
|
|
|
|
+ for (int i = 0; i < RenderAttrib::M_always; ++i) {
|
|
|
+ unit._size_loc[i] = -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (size_location >= 0) {
|
|
|
+ unit._size_loc[alpha_test_mode] = size_location;
|
|
|
+ }
|
|
|
+
|
|
|
#ifndef OPENGLES
|
|
|
// See note above in the SampledImage case.
|
|
|
for (size_t i = 0; i < _image_units.size(); ++i) {
|
|
|
- const ImageUnit &other_unit = _image_units[i];
|
|
|
+ ImageUnit &other_unit = _image_units[i];
|
|
|
if (other_unit._binding == unit._binding &&
|
|
|
other_unit._resource_id == unit._resource_id &&
|
|
|
other_unit._access == unit._access) {
|
|
|
binding = (GLint)i;
|
|
|
+ if (unit._size_loc[alpha_test_mode] >= 0) {
|
|
|
+ other_unit._size_loc[alpha_test_mode] = unit._size_loc[alpha_test_mode];
|
|
|
+ }
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
@@ -760,7 +816,9 @@ r_collect_uniforms(GLuint program,
|
|
|
binding = (GLint)_image_units.size();
|
|
|
_image_units.push_back(std::move(unit));
|
|
|
}
|
|
|
- _glgsg->_glUniform1i(location, binding);
|
|
|
+ if (location >= 0) {
|
|
|
+ _glgsg->_glUniform1i(location, binding);
|
|
|
+ }
|
|
|
#endif
|
|
|
}
|
|
|
else if (type->as_resource()) {
|
|
|
@@ -2119,6 +2177,10 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) {
|
|
|
issue_parameters(Shader::D_vertex_data);
|
|
|
}
|
|
|
|
|
|
+ // This ought to be moved elsewhere, but it's convenient to do this here for
|
|
|
+ // now since it's called before every Geom is drawn.
|
|
|
+ issue_memory_barriers();
|
|
|
+
|
|
|
_glgsg->report_my_gl_errors();
|
|
|
|
|
|
return true;
|
|
|
@@ -2202,9 +2264,7 @@ update_shader_texture_bindings(ShaderContext *prev) {
|
|
|
// return;
|
|
|
//}
|
|
|
|
|
|
-#ifndef OPENGLES
|
|
|
GLbitfield barriers = 0;
|
|
|
-#endif
|
|
|
|
|
|
ShaderInputBinding::State state;
|
|
|
state.gsg = _glgsg;
|
|
|
@@ -2239,12 +2299,6 @@ update_shader_texture_bindings(ShaderContext *prev) {
|
|
|
|
|
|
int view = _glgsg->get_current_tex_view_offset();
|
|
|
gl_tex = gtc->get_view_index(view);
|
|
|
-
|
|
|
-#ifndef OPENGLES
|
|
|
- if (gtc->needs_barrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT)) {
|
|
|
- barriers |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
|
|
|
- }
|
|
|
-#endif
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -2301,8 +2355,19 @@ update_shader_texture_bindings(ShaderContext *prev) {
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
+ if (gtc->needs_barrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT, unit._written)) {
|
|
|
+ barriers |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
|
|
|
+ }
|
|
|
+
|
|
|
_glgsg->_glBindImageTexture(i, gl_tex, bind_level, layered,
|
|
|
bind_layer, gl_access, gtc->_internal_format);
|
|
|
+
|
|
|
+ // Update the size variable, if we have one.
|
|
|
+ GLint size_loc = unit._size_loc[_alpha_test_mode];
|
|
|
+ if (size_loc != -1) {
|
|
|
+ _glgsg->_glUniform4f(size_loc, (GLfloat)gtc->_width, (GLfloat)gtc->_height,
|
|
|
+ (GLfloat)gtc->_depth, (GLfloat)gtc->_num_levels);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -2359,7 +2424,7 @@ update_shader_texture_bindings(ShaderContext *prev) {
|
|
|
#ifndef OPENGLES
|
|
|
// If it was recently written to, we will have to issue a memory barrier
|
|
|
// soon.
|
|
|
- if (gtc->needs_barrier(GL_TEXTURE_FETCH_BARRIER_BIT)) {
|
|
|
+ if (gtc->needs_barrier(GL_TEXTURE_FETCH_BARRIER_BIT, false)) {
|
|
|
barriers |= GL_TEXTURE_FETCH_BARRIER_BIT;
|
|
|
}
|
|
|
#endif
|
|
|
@@ -2396,6 +2461,13 @@ update_shader_texture_bindings(ShaderContext *prev) {
|
|
|
_glgsg->apply_texture(gtc, view);
|
|
|
_glgsg->apply_sampler(i, sampler, gtc, view);
|
|
|
}
|
|
|
+
|
|
|
+ // Update the size variable, if we have one.
|
|
|
+ GLint size_loc = unit._size_loc[_alpha_test_mode];
|
|
|
+ if (size_loc != -1) {
|
|
|
+ _glgsg->_glUniform4f(size_loc, (GLfloat)gtc->_width, (GLfloat)gtc->_height,
|
|
|
+ (GLfloat)gtc->_depth, (GLfloat)gtc->_num_levels);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
#ifndef OPENGLES
|
|
|
@@ -2403,12 +2475,12 @@ update_shader_texture_bindings(ShaderContext *prev) {
|
|
|
_glgsg->_glBindTextures(0, num_textures, textures);
|
|
|
_glgsg->_glBindSamplers(0, num_textures, samplers);
|
|
|
}
|
|
|
+#endif
|
|
|
|
|
|
if (barriers != 0) {
|
|
|
// Issue a memory barrier prior to this shader's execution.
|
|
|
_glgsg->issue_memory_barrier(barriers);
|
|
|
}
|
|
|
-#endif
|
|
|
|
|
|
_glgsg->report_my_gl_errors();
|
|
|
}
|
|
|
@@ -2423,10 +2495,38 @@ update_shader_buffer_bindings(ShaderContext *prev) {
|
|
|
state.gsg = _glgsg;
|
|
|
state.matrix_cache = &_matrix_cache[0];
|
|
|
|
|
|
- for (const StorageBlock &block : _storage_blocks) {
|
|
|
+ for (StorageBlock &block : _storage_blocks) {
|
|
|
PT(ShaderBuffer) buffer = block._binding->fetch_shader_buffer(state, block._resource_id);
|
|
|
- _glgsg->apply_shader_buffer(block._binding_index, buffer);
|
|
|
+ block._gbc = _glgsg->apply_shader_buffer(block._binding_index, buffer);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Issues memory barriers for shader buffers, should be called before a draw.
|
|
|
+ */
|
|
|
+void CLP(ShaderContext)::
|
|
|
+issue_memory_barriers() {
|
|
|
+#ifndef OPENGLES
|
|
|
+ bool barrier_needed = false;
|
|
|
+ for (StorageBlock &block : _storage_blocks) {
|
|
|
+ if (block._gbc != nullptr &&
|
|
|
+ block._gbc->_shader_storage_barrier_counter == _glgsg->_shader_storage_barrier_counter) {
|
|
|
+ barrier_needed = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (barrier_needed) {
|
|
|
+ _glgsg->issue_memory_barrier(GL_SHADER_STORAGE_BARRIER_BIT);
|
|
|
+ }
|
|
|
+
|
|
|
+ // We assume that all SSBOs will be written to, for now.
|
|
|
+ for (StorageBlock &block : _storage_blocks) {
|
|
|
+ if (block._gbc != nullptr) {
|
|
|
+ block._gbc->_shader_storage_barrier_counter = _glgsg->_shader_storage_barrier_counter;
|
|
|
+ }
|
|
|
}
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -2724,6 +2824,20 @@ create_shader(GLuint program, const ShaderModule *module, size_t mi,
|
|
|
|
|
|
ShaderModuleSpirV::InstructionStream stream = spv->_instructions;
|
|
|
|
|
|
+ // Do we need to emulate certain caps, like texture queries?
|
|
|
+ pmap<SpirVTransformPass::AccessChain, uint32_t> size_var_ids;
|
|
|
+ uint64_t supported_caps = _glgsg->get_supported_shader_capabilities();
|
|
|
+ uint64_t emulate_caps = spv->_emulatable_caps & ~supported_caps;
|
|
|
+ if (emulate_caps != 0u) {
|
|
|
+ _emulated_caps |= emulate_caps;
|
|
|
+
|
|
|
+ SpirVTransformer transformer(spv->_instructions);
|
|
|
+ SpirVEmulateTextureQueriesPass pass(emulate_caps);
|
|
|
+ transformer.run(pass);
|
|
|
+ size_var_ids = std::move(pass._size_var_ids);
|
|
|
+ stream = transformer.get_result();
|
|
|
+ }
|
|
|
+
|
|
|
if (stage != ShaderModule::Stage::FRAGMENT) {
|
|
|
alpha_test_mode = RenderAttrib::M_none;
|
|
|
}
|
|
|
@@ -2782,11 +2896,11 @@ create_shader(GLuint program, const ShaderModule *module, size_t mi,
|
|
|
// Assign names based on locations. This is important to make sure that
|
|
|
// uniforms shared between shader stages have the same name, or the
|
|
|
// compiler may start to complain about overlapping locations.
|
|
|
+ char buf[1024];
|
|
|
for (spirv_cross::VariableID id : compiler.get_active_interface_variables()) {
|
|
|
uint32_t loc = compiler.get_decoration(id, spv::DecorationLocation);
|
|
|
spv::StorageClass sc = compiler.get_storage_class(id);
|
|
|
|
|
|
- char buf[1024];
|
|
|
if (sc == spv::StorageClassUniformConstant) {
|
|
|
auto it = id_to_location.find(id);
|
|
|
if (it != id_to_location.end()) {
|
|
|
@@ -2834,6 +2948,21 @@ create_shader(GLuint program, const ShaderModule *module, size_t mi,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // Assign names to emulated texture/image size variables.
|
|
|
+ for (auto &item : size_var_ids) {
|
|
|
+ const SpirVTransformPass::AccessChain &chain = item.first;
|
|
|
+ auto it = id_to_location.find(chain._var_id);
|
|
|
+ if (it != id_to_location.end()) {
|
|
|
+ int location = it->second;
|
|
|
+ size_t size = sprintf(buf, "p%u", location);
|
|
|
+ for (size_t i = 0; i < chain.size(); ++i) {
|
|
|
+ size += sprintf(buf + size, "_%d", chain[i]);
|
|
|
+ }
|
|
|
+ strcpy(buf + size, "_s");
|
|
|
+ compiler.set_name(item.second, buf);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// For all uniform constant structs, we need to ensure we have procedural
|
|
|
// names like _m0, _m1, _m2, etc. Furthermore, we need to assign each
|
|
|
// struct a name that is guaranteed to be the same between stages, since
|
|
|
@@ -2844,7 +2973,6 @@ create_shader(GLuint program, const ShaderModule *module, size_t mi,
|
|
|
item.second->output_signature(str);
|
|
|
compiler.set_name(item.first, str.str());
|
|
|
|
|
|
- char buf[32];
|
|
|
for (size_t i = 0; i < item.second->get_num_members(); ++i) {
|
|
|
sprintf(buf, "m%d", (int)i);
|
|
|
compiler.set_member_name(item.first, i, buf);
|