filter-transform: Fix mipmapping for npot input

The filter will now automatically rescale the parent stack into the next best power of two size. With this, even non-power-of-two sources can now be mipmapped semi-correctly.

To accurately support mipmapping even for npot textures, this feature would have to be built into OBS and OBS would have to stop refusing to create textures with mipmaps that are not a power of two in size. Almost all common Direct3D 11 (except Intel) are capable of npot mipmaps at full speed, while OpenGL usually depends on the GPU and Driver used.
This commit is contained in:
Michael Fabian 'Xaymar' Dirks 2018-09-29 17:05:58 +02:00
parent 6561ea9b3c
commit da22e72da7
1 changed files with 28 additions and 8 deletions

View File

@ -393,6 +393,8 @@ void Filter::Transform::Instance::video_render(gs_effect_t* paramEffect)
uint32_t width = obs_source_get_base_width(target); uint32_t width = obs_source_get_base_width(target);
uint32_t height = obs_source_get_base_height(target); uint32_t height = obs_source_get_base_height(target);
uint32_t real_width = width;
uint32_t real_height = height;
gs_effect_t* default_effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); gs_effect_t* default_effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
@ -483,9 +485,26 @@ void Filter::Transform::Instance::video_render(gs_effect_t* paramEffect)
is_mesh_update_required = false; is_mesh_update_required = false;
} }
// Make texture a power of two compatible texture if mipmapping is enabled.
if (enable_mipmapping) {
real_width = pow(2, util::math::get_power_of_two_exponent_ceil(width));
real_height = pow(2, util::math::get_power_of_two_exponent_ceil(height));
if ((real_width >= 8192) || (real_height >= 8192)) {
// Most GPUs cap out here, so let's not go higher.
double_t aspect = double_t(width) / double_t(height);
if (aspect > 1.0) { // height < width
real_width = 8192;
real_height = real_width / aspect;
} else if (aspect < 1.0) { // width > height
real_height = 8192;
real_width = real_height * aspect;
}
}
}
// Draw previous filters to texture. // Draw previous filters to texture.
try { try {
auto op = source_rt->render(width, height); auto op = source_rt->render(real_width, real_height);
gs_set_cull_mode(GS_NEITHER); gs_set_cull_mode(GS_NEITHER);
gs_reset_blend_state(); gs_reset_blend_state();
@ -514,19 +533,20 @@ void Filter::Transform::Instance::video_render(gs_effect_t* paramEffect)
source_rt->get_texture(source_tex); source_rt->get_texture(source_tex);
if (enable_mipmapping) { if (enable_mipmapping) {
if ((!source_texture) || (source_texture->get_width() != width) || (source_texture->get_height() != height)) { if ((!source_texture) || (source_texture->get_width() != real_width)
size_t mip_levels = 1; || (source_texture->get_height() != real_height)) {
if (util::math::is_power_of_two(width) && util::math::is_power_of_two(height)) { size_t mip_levels = 0;
size_t w_level = util::math::get_power_of_two_floor(width); if (util::math::is_power_of_two(real_width) && util::math::is_power_of_two(real_height)) {
size_t h_level = util::math::get_power_of_two_floor(height); size_t w_level = util::math::get_power_of_two_exponent_ceil(real_width);
if (h_level < w_level) { size_t h_level = util::math::get_power_of_two_exponent_ceil(real_height);
if (h_level > w_level) {
mip_levels = h_level; mip_levels = h_level;
} else { } else {
mip_levels = w_level; mip_levels = w_level;
} }
} }
source_texture = std::make_shared<gs::texture>(width, height, GS_RGBA, mip_levels, nullptr, source_texture = std::make_shared<gs::texture>(real_width, real_height, GS_RGBA, 1 + mip_levels, nullptr,
gs::texture::flags::BuildMipMaps); gs::texture::flags::BuildMipMaps);
} }