+
+ for (unsigned i = 0; i < phase->effects.size(); ++i) {
+ Node *node = phase->effects[i];
+ node->effect->clear_gl_state();
+ }
+
+ if (!last_phase) {
+ resource_pool->release_fbo(fbo);
+ }
+}
+
+void EffectChain::setup_uniforms(Phase *phase)
+{
+ char *ubo_data = phase->ubo_data.empty() ? NULL : &phase->ubo_data[0];
+
+ for (size_t i = 0; i < phase->uniforms_sampler2d.size(); ++i) {
+ const Uniform<int> &uniform = phase->uniforms_sampler2d[i];
+ if (uniform.location != GL_INVALID_INDEX) {
+ glUniform1iv(uniform.location, uniform.num_values, uniform.value);
+ }
+ assert(uniform.ubo_offset == -1); // Samplers don't go into UBOs.
+ }
+ for (size_t i = 0; i < phase->uniforms_bool.size(); ++i) {
+ const Uniform<bool> &uniform = phase->uniforms_bool[i];
+ assert(uniform.num_values == 1);
+ if (uniform.location != GL_INVALID_INDEX) {
+ glUniform1i(uniform.location, *uniform.value);
+ }
+ if (uniform.ubo_offset != -1) {
+ GLint int_val = *uniform.value;
+ memcpy(ubo_data + uniform.ubo_offset, &int_val, sizeof(int_val));
+ }
+ }
+ for (size_t i = 0; i < phase->uniforms_int.size(); ++i) {
+ const Uniform<int> &uniform = phase->uniforms_int[i];
+ if (uniform.location != GL_INVALID_INDEX) {
+ glUniform1iv(uniform.location, uniform.num_values, uniform.value);
+ }
+ if (uniform.ubo_offset != -1) {
+ memcpy(ubo_data + uniform.ubo_offset, uniform.value, uniform.ubo_num_elem * sizeof(*uniform.value));
+ }
+ }
+ for (size_t i = 0; i < phase->uniforms_float.size(); ++i) {
+ const Uniform<float> &uniform = phase->uniforms_float[i];
+ if (uniform.location != GL_INVALID_INDEX) {
+ glUniform1fv(uniform.location, uniform.num_values, uniform.value);
+ }
+ if (uniform.ubo_offset != -1) {
+ memcpy(ubo_data + uniform.ubo_offset, uniform.value, uniform.ubo_num_elem * sizeof(*uniform.value));
+ }
+ }
+ for (size_t i = 0; i < phase->uniforms_vec2.size(); ++i) {
+ const Uniform<float> &uniform = phase->uniforms_vec2[i];
+ if (uniform.location != GL_INVALID_INDEX) {
+ glUniform2fv(uniform.location, uniform.ubo_num_elem, uniform.value);
+ }
+ if (uniform.ubo_offset != -1) {
+ memcpy(ubo_data + uniform.ubo_offset, uniform.value, uniform.ubo_num_elem * 2 * sizeof(*uniform.value));
+ }
+ }
+ for (size_t i = 0; i < phase->uniforms_vec3.size(); ++i) {
+ const Uniform<float> &uniform = phase->uniforms_vec3[i];
+ if (uniform.location != GL_INVALID_INDEX) {
+ glUniform3fv(uniform.location, uniform.ubo_num_elem, uniform.value);
+ }
+ if (uniform.ubo_offset != -1) {
+ memcpy(ubo_data + uniform.ubo_offset, uniform.value, uniform.ubo_num_elem * 3 * sizeof(*uniform.value));
+ }
+ }
+ for (size_t i = 0; i < phase->uniforms_vec4.size(); ++i) {
+ const Uniform<float> &uniform = phase->uniforms_vec4[i];
+ if (uniform.location != GL_INVALID_INDEX) {
+ glUniform4fv(uniform.location, uniform.ubo_num_elem, uniform.value);
+ }
+ if (uniform.ubo_offset != -1) {
+ memcpy(ubo_data + uniform.ubo_offset, uniform.value, uniform.ubo_num_elem * 4 * sizeof(*uniform.value));
+ }
+ }
+ for (size_t i = 0; i < phase->uniforms_mat3.size(); ++i) {
+ const Uniform<Matrix3d> &uniform = phase->uniforms_mat3[i];
+ assert(uniform.ubo_num_elem == 1);
+ if (uniform.location != GL_INVALID_INDEX) {
+ // Convert to float (GLSL has no double matrices).
+ float matrixf[9];
+ for (unsigned y = 0; y < 3; ++y) {
+ for (unsigned x = 0; x < 3; ++x) {
+ matrixf[y + x * 3] = (*uniform.value)(y, x);
+ }
+ }
+ glUniformMatrix3fv(uniform.location, 1, GL_FALSE, matrixf);
+ }
+ if (uniform.ubo_offset != -1) {
+ // TODO
+ assert(false);
+ }
+ }
+
+ if (phase->ubo != GL_INVALID_INDEX) {
+ // TODO: Do we want to demand DSA for this?
+ glNamedBufferSubData(phase->ubo, 0, phase->ubo_data.size(), ubo_data);
+ return;
+ }
+}
+
+void EffectChain::setup_rtt_sampler(int sampler_num, bool use_mipmaps)
+{
+ glActiveTexture(GL_TEXTURE0 + sampler_num);