class IdentityEffect : public Effect {
public:
IdentityEffect() {}
- virtual string effect_type_id() const { return "IdentityEffect"; }
- string output_fragment_shader() { return read_file("identity.frag"); }
+ string effect_type_id() const override { return "IdentityEffect"; }
+ string output_fragment_shader() override { return read_file("identity.frag"); }
};
} // namespace
}
if (phase->is_compute_shader) {
- frag_shader.append(read_file("footer.compute"));
+ frag_shader.append(read_file("footer.comp"));
phase->output_node->effect->register_uniform_vec2("inv_output_size", (float *)&phase->inv_output_size);
phase->output_node->effect->register_uniform_vec2("output_texcoord_adjust", (float *)&phase->output_texcoord_adjust);
} else {
set<Phase *> generated_mipmaps;
- // We choose the simplest option of having one texture per output,
- // since otherwise this turns into an (albeit simple) register allocation problem.
+ // We keep one texture per output, but only for as long as we actually have any
+ // phases that need it as an input. (We don't make any effort to reorder phases
+ // to minimize the number of textures in play, as register allocation can be
+ // complicated and we rarely have much to gain, since our graphs are typically
+ // pretty linear.)
map<Phase *, GLuint> output_textures;
+ map<Phase *, int> ref_counts;
+ for (Phase *phase : phases) {
+ for (Phase *input : phase->inputs) {
+ ++ref_counts[input];
+ }
+ }
size_t num_phases = phases.size();
if (destinations.empty()) {
// Find a texture for this phase.
inform_input_sizes(phase);
find_output_size(phase);
- GLuint tex_num = 0;
+ vector<DestinationTexture> phase_destinations;
if (!last_phase) {
- tex_num = resource_pool->create_2d_texture(intermediate_format, phase->output_width, phase->output_height);
- assert(tex_num != 0);
+ GLuint tex_num = resource_pool->create_2d_texture(intermediate_format, phase->output_width, phase->output_height);
output_textures.insert(make_pair(phase, tex_num));
+ phase_destinations.push_back(DestinationTexture{ tex_num, intermediate_format });
// The output texture needs to have valid state to be written to by a compute shader.
glActiveTexture(GL_TEXTURE0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
check_error();
} else if (phase->is_compute_shader) {
- // TODO: Support more than one destination.
assert(!destinations.empty());
- tex_num = destinations[0].texnum;
- assert(destinations[0].format == GL_RGBA16F);
- assert(destinations[0].texnum != 0);
+ phase_destinations = destinations;
}
- execute_phase(phase, output_textures, tex_num, &generated_mipmaps);
+ execute_phase(phase, output_textures, phase_destinations, &generated_mipmaps);
if (do_phase_timing) {
glEndQuery(GL_TIME_ELAPSED);
}
+
+ // Drop any input textures we don't need anymore.
+ for (Phase *input : phase->inputs) {
+ assert(ref_counts[input] > 0);
+ if (--ref_counts[input] == 0) {
+ resource_pool->release_2d_texture(output_textures[input]);
+ output_textures.erase(input);
+ }
+ }
}
for (const auto &phase_and_texnum : output_textures) {
void EffectChain::execute_phase(Phase *phase,
const map<Phase *, GLuint> &output_textures,
- GLuint dest_texture,
+ const std::vector<DestinationTexture> &destinations,
set<Phase *> *generated_mipmaps)
{
// Set up RTT inputs for this phase.
// And now the output.
GLuint fbo = 0;
if (phase->is_compute_shader) {
- assert(dest_texture != 0);
+ assert(!destinations.empty());
// This is currently the only place where we use image units,
- // so we can always use 0.
+ // so we can always start at 0. TODO: Support multiple destinations.
phase->outbuf_image_unit = 0;
- glBindImageTexture(phase->outbuf_image_unit, dest_texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA16F);
+ glBindImageTexture(phase->outbuf_image_unit, destinations[0].texnum, 0, GL_FALSE, 0, GL_WRITE_ONLY, destinations[0].format);
check_error();
phase->inv_output_size.x = 1.0f / phase->output_width;
phase->inv_output_size.y = 1.0f / phase->output_height;
phase->output_texcoord_adjust.x = 0.5f / phase->output_width;
phase->output_texcoord_adjust.y = 0.5f / phase->output_height;
- } else if (dest_texture != 0) {
- fbo = resource_pool->create_fbo(dest_texture);
+ } else if (!destinations.empty()) {
+ assert(destinations.size() == 1);
+ fbo = resource_pool->create_fbo(destinations[0].texnum);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glViewport(0, 0, phase->output_width, phase->output_height);
}