bool ok = hpass->set_float("radius", adjusted_radius);
ok |= hpass->set_int("width", mipmap_width);
ok |= hpass->set_int("height", mipmap_height);
+ ok |= hpass->set_int("virtual_width", mipmap_width);
+ ok |= hpass->set_int("virtual_height", mipmap_height);
ok |= vpass->set_float("radius", adjusted_radius);
ok |= vpass->set_int("width", mipmap_width);
ok |= vpass->set_int("height", mipmap_height);
+ ok |= vpass->set_int("virtual_width", input_width);
+ ok |= vpass->set_int("virtual_height", input_height);
assert(ok);
}
register_int("direction", (int *)&direction);
register_int("width", &width);
register_int("height", &height);
+ register_int("virtual_width", &virtual_width);
+ register_int("virtual_height", &virtual_height);
}
std::string SingleBlurPassEffect::output_fragment_shader()
}
virtual bool changes_output_size() const { return true; }
- virtual void get_output_size(unsigned *width, unsigned *height) const {
+ virtual void get_output_size(unsigned *width, unsigned *height, unsigned *virtual_width, unsigned *virtual_height) const {
*width = this->width;
*height = this->height;
+ *virtual_width = this->virtual_width;
+ *virtual_height = this->virtual_height;
}
void set_gl_state(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num);
BlurEffect *parent;
float radius;
Direction direction;
- int width, height;
+ int width, height, virtual_width, virtual_height;
};
#endif // !defined(_BLUR_EFFECT_H)
virtual bool changes_output_size() const { return false; }
// If changes_output_size() is true, you must implement this to tell
- // the framework what output size you want.
+ // the framework what output size you want. Also, you can set a
+ // virtual width/height, which is the size the next effect (if any)
+ // will _think_ your data is in. This is primarily useful if you are
+ // relying on getting OpenGL's bilinear resizing for free; otherwise,
+ // your virtual_width/virtual_height should be the same as width/height.
//
// Note that it is explicitly allowed to change width and height
// from frame to frame; EffectChain will reallocate textures as needed.
- virtual void get_output_size(unsigned *width, unsigned *height) const {
+ virtual void get_output_size(unsigned *width, unsigned *height,
+ unsigned *virtual_width, unsigned *virtual_height) const {
assert(false);
}
}
for (unsigned i = 0; i < phase->inputs.size(); ++i) {
Node *input = phase->inputs[i];
- input->output_width = input->phase->output_width;
- input->output_height = input->phase->output_height;
+ input->output_width = input->phase->virtual_output_width;
+ input->output_height = input->phase->virtual_output_height;
assert(input->output_width != 0);
assert(input->output_height != 0);
}
// If the last effect explicitly sets an output size, use that.
if (output_node->effect->changes_output_size()) {
- output_node->effect->get_output_size(&phase->output_width, &phase->output_height);
+ output_node->effect->get_output_size(&phase->output_width, &phase->output_height,
+ &phase->virtual_output_width, &phase->virtual_output_height);
return;
}
assert(input->phase->output_width != 0);
assert(input->phase->output_height != 0);
if (output_width == 0 && output_height == 0) {
- output_width = input->phase->output_width;
- output_height = input->phase->output_height;
- } else if (output_width != input->phase->output_width ||
- output_height != input->phase->output_height) {
+ output_width = input->phase->virtual_output_width;
+ output_height = input->phase->virtual_output_height;
+ } else if (output_width != input->phase->virtual_output_width ||
+ output_height != input->phase->virtual_output_height) {
all_inputs_same_size = false;
}
}
if (all_inputs_same_size) {
assert(output_width != 0);
assert(output_height != 0);
- phase->output_width = output_width;
- phase->output_height = output_height;
+ phase->virtual_output_width = phase->output_width = output_width;
+ phase->virtual_output_height = phase->output_height = output_height;
return;
}
}
assert(output_width != 0);
assert(output_height != 0);
- phase->output_width = output_width;
- phase->output_height = output_height;
+ phase->virtual_output_width = phase->output_width = output_width;
+ phase->virtual_output_height = phase->output_height = output_height;
}
void EffectChain::sort_all_nodes_topologically()
std::vector<Node *> inputs;
std::vector<Node *> effects; // In order.
- unsigned output_width, output_height;
+ unsigned output_width, output_height, virtual_output_width, virtual_output_height;
};
class EffectChain {
EXPECT_EQ(9, input_store->input_width);
EXPECT_EQ(7, input_store->input_height);
}
+
+// An effect that does nothing except changing its output sizes.
+class VirtualResizeEffect : public Effect {
+public:
+ VirtualResizeEffect(int width, int height, int virtual_width, int virtual_height)
+ : width(width),
+ height(height),
+ virtual_width(virtual_width),
+ virtual_height(virtual_height) {}
+ virtual std::string effect_type_id() const { return "VirtualResizeEffect"; }
+ std::string output_fragment_shader() { return read_file("identity.frag"); }
+
+ virtual bool changes_output_size() const { return true; }
+
+ virtual void get_output_size(unsigned *width, unsigned *height,
+ unsigned *virtual_width, unsigned *virtual_height) const {
+ *width = this->width;
+ *height = this->height;
+ *virtual_width = this->virtual_width;
+ *virtual_height = this->virtual_height;
+ }
+
+private:
+ int width, height, virtual_width, virtual_height;
+};
+
+TEST(EffectChainTest, VirtualSizeIsSentOnToInputs) {
+ const int size = 2, bigger_size = 3;
+ float data[size * size] = {
+ 1.0f, 0.0f,
+ 0.0f, 1.0f,
+ };
+ float out_data[size * size];
+
+ EffectChainTester tester(data, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+ SizeStoringEffect *size_store = new SizeStoringEffect();
+
+ tester.get_chain()->add_effect(new VirtualResizeEffect(size, size, bigger_size, bigger_size));
+ tester.get_chain()->add_effect(size_store);
+ tester.get_chain()->add_effect(new VirtualResizeEffect(size, size, size, size));
+ tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+ EXPECT_EQ(bigger_size, size_store->input_width);
+ EXPECT_EQ(bigger_size, size_store->input_height);
+
+ // If the resize is implemented as non-virtual, we'll fail here,
+ // since bilinear scaling from 2x2 → 3x3 → 2x2 is not very exact.
+ expect_equal(data, out_data, size, size);
+}
return INPUT_AND_OUTPUT_ALPHA_PREMULTIPLIED;
}
-void PaddingEffect::get_output_size(unsigned *width, unsigned *height) const
+void PaddingEffect::get_output_size(unsigned *width, unsigned *height, unsigned *virtual_width, unsigned *virtual_height) const
{
- *width = output_width;
- *height = output_height;
+ *virtual_width = *width = output_width;
+ *virtual_height = *height = output_height;
}
void PaddingEffect::inform_input_size(unsigned input_num, unsigned width, unsigned height)
virtual AlphaHandling alpha_handling() const;
virtual bool changes_output_size() const { return true; }
- virtual void get_output_size(unsigned *width, unsigned *height) const;
+ virtual void get_output_size(unsigned *width, unsigned *height, unsigned *virtual_width, unsigned *virtual_height) const;
virtual void inform_input_size(unsigned input_num, unsigned width, unsigned height);
private:
}
virtual bool changes_output_size() const { return true; }
- virtual void get_output_size(unsigned *width, unsigned *height) const {
- *width = this->output_width;
- *height = this->output_height;
+ virtual void get_output_size(unsigned *width, unsigned *height, unsigned *virtual_width, unsigned *virtual_height) const {
+ *virtual_width = *width = this->output_width;
+ *virtual_height = *height = this->output_height;
}
void set_gl_state(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num);
return read_file("identity.frag");
}
-void ResizeEffect::get_output_size(unsigned *width, unsigned *height) const
+void ResizeEffect::get_output_size(unsigned *width, unsigned *height, unsigned *virtual_width, unsigned *virtual_height) const
{
- *width = this->width;
- *height = this->height;
+ *virtual_width = *width = this->width;
+ *virtual_height = *height = this->height;
}
virtual bool needs_mipmaps() const { return true; }
virtual bool changes_output_size() const { return true; }
- virtual void get_output_size(unsigned *width, unsigned *height) const;
+ virtual void get_output_size(unsigned *width, unsigned *height, unsigned *virtual_width, unsigned *virtual_height) const;
private:
int width, height;