diff --git a/data/effects/mipgen.effect b/data/effects/mipgen.effect index 5cf23446..1852dfac 100644 --- a/data/effects/mipgen.effect +++ b/data/effects/mipgen.effect @@ -1,172 +1,35 @@ uniform float4x4 ViewProj; uniform texture2d image; -uniform int level; uniform float2 imageTexel; -uniform float strength; +uniform int level; -sampler_state pointSampler { - Filter = Point; - AddressU = Clamp; - AddressV = Clamp; -}; - -sampler_state linearSampler { +sampler_state def_sampler { Filter = Linear; AddressU = Clamp; AddressV = Clamp; }; -struct VertDataIn { +struct VertexData { float4 pos : POSITION; float2 uv : TEXCOORD0; }; -struct VertDataOut { - float4 pos : POSITION; - float2 uv : TEXCOORD0; -}; - -VertDataOut VSDefault(VertDataIn v_in) +VertexData VSDefault(VertexData vtx) { - VertDataOut vert_out; - vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); - vert_out.uv = v_in.uv; - return vert_out; + vtx.pos = mul(float4(vtx.pos.xyz, 1.0), ViewProj); + return vtx; } -float4 PSPoint(VertDataOut v_in) : TARGET +float4 PSDefault(VertexData vtx) : TARGET { - return image.SampleLevel(pointSampler, v_in.uv, level); + return image.SampleLevel(def_sampler, vtx.uv, level); } -float4 PSLinear(VertDataOut v_in) : TARGET -{ - return image.SampleLevel(linearSampler, v_in.uv, level); -} - -float4 PSSharpen(VertDataOut v_in) : TARGET -{ - float2 ul, ur, dl, dr, u, d, l, r; - ul = float2(-imageTexel.x, -imageTexel.y); - ur = float2(imageTexel.x, -imageTexel.y); - dl = -ur; - dr = -ul; - u = float2(0, -imageTexel.y); - d = -u; - l = float2(-imageTexel.x, 0); - r = -l; - - float4 tl, tc, tr, cl, cc, cr, bl, bc, br; - tl = image.SampleLevel(pointSampler, v_in.uv + ul, level); - tc = image.SampleLevel(pointSampler, v_in.uv + u, level); - tr = image.SampleLevel(pointSampler, v_in.uv + ur, level); - cl = image.SampleLevel(pointSampler, v_in.uv + l, level); - cc = image.SampleLevel(pointSampler, v_in.uv, level); - cr = image.SampleLevel(pointSampler, v_in.uv + r, level); - bl = image.SampleLevel(pointSampler, v_in.uv + dl, level); - bc = image.SampleLevel(pointSampler, v_in.uv + d, level); - br = image.SampleLevel(pointSampler, v_in.uv + dr, level); - - float kernel1, kernel2, kernel3; - kernel1 = -0.25 * strength; - kernel2 = -0.50 * strength; - kernel3 = abs(kernel1 * 4) + abs(kernel2 * 4) + 1; - - return (tl * kernel1) + (tr * kernel1) + (bl * kernel1) + (br * kernel1) + (cl * kernel2) + (cr * kernel2) + (tc * kernel2) + (bc * kernel2) + (cc * kernel3); -} - -float4 PSSmoothen(VertDataOut v_in) : TARGET -{ - // If we use linear sampling, we can get away with just 4 total sampler queries. - // However this is not a cheap implementation, it's just meant to be accurate so we do each sampler query and rely on the compiler. - - float3 smoothKernel3 = float3(0.0574428, 0.0947072, 0.3914000); - float2 ul, ur, dl, dr, u, d, l, r; - float4 tl, tc, tr, cl, cc, cr, bl, bc, br; - float limitstr = clamp(strength, 0.0, 1.0); - - ul = float2(-imageTexel.x, -imageTexel.y); - ur = float2(imageTexel.x, -imageTexel.y); - dl = -ur; - dr = -ul; - u = float2(0, -imageTexel.y); - d = -u; - l = float2(-imageTexel.x, 0); - r = -l; - - tl = image.SampleLevel(pointSampler, v_in.uv + ul * limitstr, level) * smoothKernel3[0]; - tc = image.SampleLevel(pointSampler, v_in.uv + u * limitstr, level) * smoothKernel3[1]; - tr = image.SampleLevel(pointSampler, v_in.uv + ur * limitstr, level) * smoothKernel3[0]; - cl = image.SampleLevel(pointSampler, v_in.uv + l * limitstr, level) * smoothKernel3[1]; - cc = image.SampleLevel(pointSampler, v_in.uv, level) * smoothKernel3[2]; - cr = image.SampleLevel(pointSampler, v_in.uv + r * limitstr, level) * smoothKernel3[1]; - bl = image.SampleLevel(pointSampler, v_in.uv + dl * limitstr, level) * smoothKernel3[0]; - bc = image.SampleLevel(pointSampler, v_in.uv + d * limitstr, level) * smoothKernel3[1]; - br = image.SampleLevel(pointSampler, v_in.uv + dr * limitstr, level) * smoothKernel3[0]; - - return tl + tc + tr + cl + cc + cr + bl + bc + br; -} - -float4 PSBicubic(VertDataOut v_in) : TARGET -{ - return float4(1.0, 0.0, 1.0, 1.0); -} - -float4 PSLanczos(VertDataOut v_in) : TARGET -{ - return float4(1.0, 0.0, 1.0, 1.0); -} - -technique Point +technique Draw { pass { - vertex_shader = VSDefault(v_in); - pixel_shader = PSPoint(v_in); - } -} - -technique Linear -{ - pass - { - vertex_shader = VSDefault(v_in); - pixel_shader = PSLinear(v_in); - } -} - -technique Sharpen -{ - pass - { - vertex_shader = VSDefault(v_in); - pixel_shader = PSSharpen(v_in); - } -} - -technique Smoothen -{ - pass - { - vertex_shader = VSDefault(v_in); - pixel_shader = PSSmoothen(v_in); - } -} - -technique Bicubic -{ - pass - { - vertex_shader = VSDefault(v_in); - pixel_shader = PSBicubic(v_in); - } -} - -technique Lanczos -{ - pass - { - vertex_shader = VSDefault(v_in); - pixel_shader = PSLanczos(v_in); + vertex_shader = VSDefault(vtx); + pixel_shader = PSDefault(vtx); } } diff --git a/source/filters/filter-transform.cpp b/source/filters/filter-transform.cpp index 7c0cc373..0543a8a1 100644 --- a/source/filters/filter-transform.cpp +++ b/source/filters/filter-transform.cpp @@ -81,9 +81,8 @@ enum RotationOrder : int64_t { }; transform_instance::transform_instance(obs_data_t* data, obs_source_t* context) - : obs::source_instance(data, context), _cache_rendered(), _mipmap_enabled(), _mipmap_strength(), - _mipmap_generator(), _source_rendered(), _source_size(), _update_mesh(), _rotation_order(), - _camera_orthographic(), _camera_fov() + : obs::source_instance(data, context), _cache_rendered(), _mipmap_enabled(), _source_rendered(), _source_size(), + _update_mesh(), _rotation_order(), _camera_orthographic(), _camera_fov() { _cache_rt = std::make_shared(GS_RGBA, GS_ZS_NONE); _source_rt = std::make_shared(GS_RGBA, GS_ZS_NONE); @@ -149,9 +148,7 @@ void transform_instance::update(obs_data_t* settings) _shear->z = 0.0f; // Mipmapping - _mipmap_enabled = obs_data_get_bool(settings, ST_MIPMAPPING); - _mipmap_strength = obs_data_get_double(settings, S_MIPGENERATOR_INTENSITY); - _mipmap_generator = static_cast(obs_data_get_int(settings, S_MIPGENERATOR)); + _mipmap_enabled = obs_data_get_bool(settings, ST_MIPMAPPING); _update_mesh = true; } @@ -360,15 +357,13 @@ void transform_instance::video_render(gs_effect_t* effect) _mipmap_texture = std::make_shared(cache_width, cache_height, GS_RGBA, mip_levels, nullptr, gs::texture::flags::None); } - _mipmapper.rebuild(_cache_texture, _mipmap_texture, _mipmap_generator, float_t(_mipmap_strength)); + _mipmapper.rebuild(_cache_texture, _mipmap_texture); _mipmap_rendered = true; - } else { - _mipmap_texture = _cache_texture; - } - if (!_mipmap_texture) { - obs_source_skip_video_filter(_self); - return; + if (!_mipmap_texture) { + obs_source_skip_video_filter(_self); + return; + } } { @@ -400,7 +395,9 @@ void transform_instance::video_render(gs_effect_t* effect) gs_load_vertexbuffer(_vertex_buffer->update(false)); gs_load_indexbuffer(nullptr); gs_effect_set_texture(gs_effect_get_param_by_name(default_effect, "image"), - _mipmap_enabled ? _mipmap_texture->get_object() : _cache_texture->get_object()); + _mipmap_enabled + ? (_mipmap_texture ? _mipmap_texture->get_object() : _cache_texture->get_object()) + : _cache_texture->get_object()); while (gs_effect_loop(default_effect, "Draw")) { gs_draw(GS_TRISTRIP, 0, 4); } @@ -455,10 +452,7 @@ void transform_factory::get_defaults2(obs_data_t* settings) obs_data_set_default_double(settings, ST_SCALE_Y, 100); obs_data_set_default_double(settings, ST_SHEAR_X, 0); obs_data_set_default_double(settings, ST_SHEAR_Y, 0); - obs_data_set_default_bool(settings, S_ADVANCED, false); obs_data_set_default_bool(settings, ST_MIPMAPPING, false); - obs_data_set_default_int(settings, S_MIPGENERATOR, static_cast(gs::mipmapper::generator::Linear)); - obs_data_set_default_double(settings, S_MIPGENERATOR_INTENSITY, 100.0); } static bool modified_properties(obs_properties_t* pr, obs_property_t*, obs_data_t* d) noexcept @@ -474,10 +468,6 @@ try { break; } - bool advancedVisible = obs_data_get_bool(d, S_ADVANCED); - obs_property_set_visible(obs_properties_get(pr, ST_ROTATION_ORDER), advancedVisible); - obs_property_set_visible(obs_properties_get(pr, ST_MIPMAPPING), advancedVisible); - return true; } catch (const std::exception& ex) { LOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what()); @@ -537,18 +527,6 @@ obs_properties_t* transform_factory::get_properties2(transform_instance* data) } } - { // Order - auto p = obs_properties_add_list(grp, ST_ROTATION_ORDER, D_TRANSLATE(ST_ROTATION_ORDER), - OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_ROTATION_ORDER))); - obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_XYZ), RotationOrder::XYZ); - obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_XZY), RotationOrder::XZY); - obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_YXZ), RotationOrder::YXZ); - obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_YZX), RotationOrder::YZX); - obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_ZXY), RotationOrder::ZXY); - obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_ZYX), RotationOrder::ZYX); - } - obs_properties_add_group(pr, ST_ROTATION, D_TRANSLATE(ST_ROTATION), OBS_GROUP_NORMAL, grp); } { // Scale @@ -576,41 +554,26 @@ obs_properties_t* transform_factory::get_properties2(transform_instance* data) obs_properties_add_group(pr, ST_SHEAR, D_TRANSLATE(ST_SHEAR), OBS_GROUP_NORMAL, grp); } - { - auto p = obs_properties_add_bool(pr, S_ADVANCED, D_TRANSLATE(S_ADVANCED)); - obs_property_set_modified_callback(p, modified_properties); - } - { auto grp = obs_properties_create(); + obs_properties_add_group(pr, S_ADVANCED, D_TRANSLATE(S_ADVANCED), OBS_GROUP_NORMAL, grp); - { - auto p = obs_properties_add_list(grp, S_MIPGENERATOR, D_TRANSLATE(S_MIPGENERATOR), OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_INT); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(S_MIPGENERATOR))); - obs_property_list_add_int(p, D_TRANSLATE(S_MIPGENERATOR_POINT), (long long)gs::mipmapper::generator::Point); - obs_property_list_add_int(p, D_TRANSLATE(S_MIPGENERATOR_LINEAR), - (long long)gs::mipmapper::generator::Linear); - obs_property_list_add_int(p, D_TRANSLATE(S_MIPGENERATOR_SHARPEN), - (long long)gs::mipmapper::generator::Sharpen); - obs_property_list_add_int(p, D_TRANSLATE(S_MIPGENERATOR_SMOOTHEN), - (long long)gs::mipmapper::generator::Smoothen); - obs_property_list_add_int(p, D_TRANSLATE(S_MIPGENERATOR_BICUBIC), - (long long)gs::mipmapper::generator::Bicubic); - obs_property_list_add_int(p, D_TRANSLATE(S_MIPGENERATOR_LANCZOS), - (long long)gs::mipmapper::generator::Lanczos); - } - { - auto p = obs_properties_add_float_slider(grp, S_MIPGENERATOR_INTENSITY, - D_TRANSLATE(S_MIPGENERATOR_INTENSITY), 0.0, 1000.0, 0.01); - obs_property_set_long_description(p, D_TRANSLATE(D_DESC(S_MIPGENERATOR_INTENSITY))); - obs_property_float_set_suffix(p, "%"); - } - - { - auto p = obs_properties_add_group(pr, ST_MIPMAPPING, D_TRANSLATE(ST_MIPMAPPING), OBS_GROUP_CHECKABLE, grp); + { // Mipmapping + auto p = obs_properties_add_bool(grp, ST_MIPMAPPING, D_TRANSLATE(ST_MIPMAPPING)); obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_MIPMAPPING))); } + + { // Order + auto p = obs_properties_add_list(grp, ST_ROTATION_ORDER, D_TRANSLATE(ST_ROTATION_ORDER), + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_set_long_description(p, D_TRANSLATE(D_DESC(ST_ROTATION_ORDER))); + obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_XYZ), RotationOrder::XYZ); + obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_XZY), RotationOrder::XZY); + obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_YXZ), RotationOrder::YXZ); + obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_YZX), RotationOrder::YZX); + obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_ZXY), RotationOrder::ZXY); + obs_property_list_add_int(p, D_TRANSLATE(ST_ROTATION_ORDER_ZYX), RotationOrder::ZYX); + } } return pr; diff --git a/source/filters/filter-transform.hpp b/source/filters/filter-transform.hpp index 9e54a659..b0ff8477 100644 --- a/source/filters/filter-transform.hpp +++ b/source/filters/filter-transform.hpp @@ -36,8 +36,6 @@ namespace streamfx::filter::transform { // Mip-mapping bool _mipmap_enabled; bool _mipmap_rendered; - double_t _mipmap_strength; - gs::mipmapper::generator _mipmap_generator; gs::mipmapper _mipmapper; std::shared_ptr _mipmap_texture; diff --git a/source/obs/gs/gs-mipmapper.cpp b/source/obs/gs/gs-mipmapper.cpp index 7e3773cb..55715aec 100644 --- a/source/obs/gs/gs-mipmapper.cpp +++ b/source/obs/gs/gs-mipmapper.cpp @@ -22,46 +22,18 @@ #include "obs/gs/gs-helper.hpp" #include "plugin.hpp" -#if defined(WIN32) || defined(WIN64) +#ifdef _WIN32 #ifdef _MSC_VER #pragma warning(push) -#pragma warning(disable : 5039) +#pragma warning(disable : 4201 4365 5039) #endif #include -#ifdef _MSC_VER -#pragma warning(pop) -#endif -#endif - -// Here be dragons! -// This is to add support for mipmap generation which is by default not possible with libobs. -// OBS hides a ton of possible things from us, which we'd have to simulate - or just hack around. -struct graphics_subsystem { - void* module; - gs_device_t* device; - // No other fields required. -}; - -#if defined(WIN32) || defined(WIN64) -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4201 4365) -#endif +#include #include #include -#include #ifdef _MSC_VER #pragma warning(pop) #endif - -// Slaughtered copy of d3d11-subsystem.hpp gs_device. We only need up to device and context, the rest is "unknown" to us. -struct gs_d3d11_device { - ComPtr factory; - ComPtr adapter; - ComPtr device; - ComPtr context; - // No other fields required. -}; #endif gs::mipmapper::~mipmapper() @@ -73,200 +45,182 @@ gs::mipmapper::~mipmapper() gs::mipmapper::mipmapper() { - _vb = std::make_unique(uint32_t(6u), std::uint8_t(1u)); - auto v0 = _vb->at(0); - v0.position->x = 0; - v0.position->y = 0; - v0.uv[0]->x = 0; - v0.uv[0]->y = 0; + _vb = std::make_unique(uint32_t(3u), std::uint8_t(1u)); - auto v1 = _vb->at(1); - auto v4 = _vb->at(4); - v4.position->x = v1.position->x = 1.0; - v4.position->y = v1.position->y = 0; - v4.uv[0]->x = v1.uv[0]->x = 1.0; - v4.uv[0]->y = v1.uv[0]->y = 0; - - auto v2 = _vb->at(2); - auto v3 = _vb->at(3); - v3.position->x = v2.position->x = 0; - v3.position->y = v2.position->y = 1.0; - v3.uv[0]->x = v2.uv[0]->x = 0; - v3.uv[0]->y = v2.uv[0]->y = 1.0; - - auto v5 = _vb->at(5); - v5.position->x = 1.0; - v5.position->y = 1.0; - v5.uv[0]->x = 1.0; - v5.uv[0]->y = 1.0; + { + auto vtx = _vb->at(0); + vtx.position->x = 0; + vtx.position->y = 0; + vtx.uv[0]->x = 0; + vtx.uv[0]->y = 0; + } + { + auto vtx = _vb->at(1); + vtx.position->x = 0.; + vtx.position->y = 2.; + vtx.uv[0]->x = 0.; + vtx.uv[0]->y = 2.; + } + { + auto vtx = _vb->at(2); + vtx.position->x = 2.; + vtx.position->y = 0.; + vtx.uv[0]->x = 2.; + vtx.uv[0]->y = 0.; + } _vb->update(); - char* effect_file = obs_module_file("effects/mipgen.effect"); - _effect = gs::effect::create(effect_file); - bfree(effect_file); + { + char* path = obs_module_file("effects/mipgen.effect"); + _effect = gs::effect::create(path); + bfree(path); + } } -void gs::mipmapper::rebuild(std::shared_ptr source, std::shared_ptr target, - gs::mipmapper::generator generator = gs::mipmapper::generator::Linear, - float_t strength = 1.0) +void gs::mipmapper::rebuild(std::shared_ptr source, std::shared_ptr target) { - // Here be dragons! You have been warned. + { // Validate arguments and structure. + if (!source || !target) + return; // Do nothing if source or target are missing. - // Do nothing if there is no texture given. - if (!source) { -#ifdef _DEBUG - assert(!source); -#endif - return; - } - if (!target) { -#ifdef _DEBUG - assert(!target); -#endif - return; - } - - // Ensure texture sizes match - if ((source->get_width() != target->get_width()) || (source->get_height() != target->get_height())) { - throw std::invalid_argument("source and target must have same size"); - } - - // Ensure texture types match - if ((source->get_type() != target->get_type())) { - throw std::invalid_argument("source and target must have same type"); - } - - // Ensure texture formats match - if ((source->get_color_format() != target->get_color_format())) { - throw std::invalid_argument("source and target must have same format"); + if (!_vb || !_effect) + return; // Do nothing if the necessary data failed to load. + + // Ensure texture sizes match + if ((source->get_width() != target->get_width()) || (source->get_height() != target->get_height())) { + throw std::invalid_argument("source and target must have same size"); + } + + // Ensure texture types match + if ((source->get_type() != target->get_type())) { + throw std::invalid_argument("source and target must have same type"); + } + + // Ensure texture formats match + if ((source->get_color_format() != target->get_color_format())) { + throw std::invalid_argument("source and target must have same format"); + } } + // Get a unique lock on the graphics context. auto gctx = gs::context(); - // Copy original texture - //gs_copy_texture(target->get_object(), source->get_object()); - - // Test if we actually need to recreate the render target for a different format or at all. + // Do we need to recreate the render target for a different format? if ((!_rt) || (source->get_color_format() != _rt->get_color_format())) { _rt = std::make_unique(source->get_color_format(), GS_ZS_NONE); } - // Render -#if defined(WIN32) || defined(WIN64) - graphics_t* ctx = gs_get_context(); - gs_d3d11_device* dev = reinterpret_cast(ctx->device); + // Grab API related information. +#ifdef _WIN32 + ID3D11Device* d3d_device = nullptr; + ID3D11DeviceContext* d3d_context = nullptr; + ID3D11Resource* d3d_source = nullptr; + ID3D11Resource* d3d_target = nullptr; + if (gs_get_device_type() == GS_DEVICE_DIRECT3D_11) { + D3D11_TEXTURE2D_DESC td; + d3d_source = reinterpret_cast(gs_texture_get_obj(source->get_object())); + d3d_target = reinterpret_cast(gs_texture_get_obj(target->get_object())); + d3d_device = reinterpret_cast(gs_get_device_obj()); + d3d_device->GetImmediateContext(&d3d_context); + } #endif - int device_type = gs_get_device_type(); - std::string technique = "Draw"; - - switch (generator) { - case generator::Point: - technique = "Point"; - break; - case generator::Linear: - technique = "Linear"; - break; - case generator::Sharpen: - technique = "Sharpen"; - break; - case generator::Smoothen: - technique = "Smoothen"; - break; - case generator::Bicubic: - technique = "Bicubic"; - break; - case generator::Lanczos: - technique = "Lanczos"; - break; + if (gs_get_device_type() == GS_DEVICE_OPENGL) { + // FixMe! Implement OpenGL } - gs_load_vertexbuffer(_vb->update()); - gs_load_indexbuffer(nullptr); - + // Use different methods for different types of textures. if (source->get_type() == gs::texture::type::Normal) { - std::size_t texture_width = source->get_width(); - std::size_t texture_height = source->get_height(); - float_t texel_width = 1.0f / texture_width; - float_t texel_height = 1.0f / texture_height; - std::size_t mip_levels = 1; + while (true) { + uint32_t width = source->get_width(); + uint32_t height = source->get_height(); + size_t max_mip_level = 1; -#if defined(WIN32) || defined(WIN64) - ID3D11Texture2D* target_t2 = nullptr; - ID3D11Texture2D* source_t2 = nullptr; - if (device_type == GS_DEVICE_DIRECT3D_11) { - // We definitely have a Direct3D11 resource. - D3D11_TEXTURE2D_DESC target_t2desc; - target_t2 = reinterpret_cast(gs_texture_get_obj(target->get_object())); - source_t2 = reinterpret_cast(gs_texture_get_obj(source->get_object())); - target_t2->GetDesc(&target_t2desc); - dev->context->CopySubresourceRegion(target_t2, 0, 0, 0, 0, source_t2, 0, nullptr); - mip_levels = target_t2desc.MipLevels; - } + { + auto cctr = gs::debug_marker(gs::debug_color_azure_radiance, "Mip Level %lld", 0); +#ifdef _WIN32 + if (gs_get_device_type() == GS_DEVICE_DIRECT3D_11) { + { // Retrieve maximum mip map level. + D3D11_TEXTURE2D_DESC td; + reinterpret_cast(d3d_target)->GetDesc(&td); + max_mip_level = td.MipLevels; + } + + // Copy mip level 0 across textures. + d3d_context->CopySubresourceRegion(d3d_target, 0, 0, 0, 0, d3d_source, 0, nullptr); + } #endif - if (device_type == GS_DEVICE_OPENGL) { - // This is an OpenGL resource. - } - - // If we do not have any miplevels, just stop now. - if (mip_levels == 1) { - return; - } - - for (std::size_t mip = 1; mip < mip_levels; mip++) { - texture_width /= 2; - texture_height /= 2; - if (texture_width == 0) { - texture_width = 1; - } - if (texture_height == 0) { - texture_height = 1; + if (gs_get_device_type() == GS_DEVICE_OPENGL) { + // FixMe! Implement OpenGL + } } - texel_width = 1.0f / texture_width; - texel_height = 1.0f / texture_height; + // Do we even need to do anything here? + if (max_mip_level == 1) + break; - // Draw mipmap layer - try { - auto op = _rt->render(uint32_t(texture_width), uint32_t(texture_height)); + // Render each mip map level. + for (size_t mip = 1; mip < max_mip_level; mip++) { + auto cctr = gs::debug_marker(gs::debug_color_azure_radiance, "Mip Level %lld", mip); - gs_set_cull_mode(GS_NEITHER); + uint32_t cwidth = std::max(width >> mip, 1); + uint32_t cheight = std::max(height >> mip, 1); + float_t iwidth = 1. / static_cast(cwidth); + float_t iheight = 1. / static_cast(cheight); + + // Set up rendering state. + gs_load_vertexbuffer(_vb->update(false)); + gs_load_indexbuffer(nullptr); + gs_blend_state_push(); gs_reset_blend_state(); - gs_blend_function_separate(gs_blend_type::GS_BLEND_ONE, gs_blend_type::GS_BLEND_ZERO, - gs_blend_type::GS_BLEND_ONE, gs_blend_type::GS_BLEND_ZERO); + gs_enable_blending(false); + gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO); + gs_enable_color(true, true, true, true); gs_enable_depth_test(false); gs_enable_stencil_test(false); gs_enable_stencil_write(false); - gs_enable_color(true, true, true, true); - gs_ortho(0, 1, 0, 1, -1, 1); + gs_set_cull_mode(GS_NEITHER); + try { + auto op = _rt->render(width, height); + gs_set_viewport(0, 0, cwidth, cheight); + gs_ortho(0, 1, 0, 1, 0, 1); - vec4 black; - vec4_zero(&black); - gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &black, 0, 0); + vec4 black = {1., 1., 1., 1}; + gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &black, 0, 0); - _effect.get_parameter("image").set_texture(target); - _effect.get_parameter("level").set_int(int32_t(mip - 1)); - _effect.get_parameter("imageTexel").set_float2(texel_width, texel_height); - _effect.get_parameter("strength").set_float(strength); - - while (gs_effect_loop(_effect.get_object(), technique.c_str())) { - gs_draw(gs_draw_mode::GS_TRIS, 0, _vb->size()); + _effect.get_parameter("image").set_texture(target); + _effect.get_parameter("imageTexel").set_float2(iwidth, iheight); + _effect.get_parameter("level").set_int(int32_t(mip - 1)); + while (gs_effect_loop(_effect.get_object(), "Draw")) { + gs_draw(gs_draw_mode::GS_TRIS, 0, _vb->size()); + } + } catch (...) { } - } catch (...) { - LOG_ERROR("Failed to render mipmap layer."); - } -#if defined(WIN32) || defined(WIN64) - if (device_type == GS_DEVICE_DIRECT3D_11) { - // Copy - ID3D11Texture2D* rt = reinterpret_cast(gs_texture_get_obj(_rt->get_object())); - std::uint32_t level = uint32_t(D3D11CalcSubresource(UINT(mip), 0, UINT(mip_levels))); - dev->context->CopySubresourceRegion(target_t2, level, 0, 0, 0, rt, 0, NULL); - } + // Clean up rendering state. + gs_load_indexbuffer(nullptr); + gs_load_vertexbuffer(nullptr); + gs_blend_state_pop(); + + // Copy from the render target to the target mip level. +#ifdef _WIN32 + if (gs_get_device_type() == GS_DEVICE_DIRECT3D_11) { + ID3D11Texture2D* rtt = + reinterpret_cast(gs_texture_get_obj(_rt->get_texture()->get_object())); + std::uint32_t level = uint32_t(D3D11CalcSubresource(UINT(mip), 0, UINT(max_mip_level))); + + D3D11_BOX box = {0, 0, 0, cwidth, cheight, 1}; + d3d_context->CopySubresourceRegion(d3d_target, level, 0, 0, 0, rtt, 0, &box); + } #endif - } - } + if (gs_get_device_type() == GS_DEVICE_OPENGL) { + // FixMe! Implement OpenGL + } + } - gs_load_indexbuffer(nullptr); - gs_load_vertexbuffer(nullptr); + break; + } + } else { + throw std::runtime_error("Texture type is not supported by mipmapping yet."); + } } diff --git a/source/obs/gs/gs-mipmapper.hpp b/source/obs/gs/gs-mipmapper.hpp index 1509e585..49bd6180 100644 --- a/source/obs/gs/gs-mipmapper.hpp +++ b/source/obs/gs/gs-mipmapper.hpp @@ -24,27 +24,28 @@ #include "gs-texture.hpp" #include "gs-vertexbuffer.hpp" +/* gs::mipmapper is an attempt at adding dynamic mip-map generation to a software + * which only supports static mip-maps. It is effectively an incredibly bad hack + * instead of a proper solution - can break any time and likely already has. + * + * Needless to say, dynamic mip-map generation costs a lot of GPU time, especially + * when things need to be synchronized. In the ideal case we would just render + * straight to the mip level, but this is not possible in DirectX 11 and OpenGL. + * + * So instead we render to a render target and copy from there to the actual + * resource. Super wasteful, but what else can we actually do? + */ + namespace gs { class mipmapper { std::unique_ptr _vb; std::unique_ptr _rt; gs::effect _effect; - public: - enum class generator : std::uint8_t { - Point, - Linear, - Sharpen, - Smoothen, - Bicubic, - Lanczos, - }; - public: ~mipmapper(); mipmapper(); - void rebuild(std::shared_ptr source, std::shared_ptr target, - gs::mipmapper::generator generator, float_t strength); + void rebuild(std::shared_ptr source, std::shared_ptr target); }; } // namespace gs diff --git a/source/strings.hpp b/source/strings.hpp index dbb7cec1..7f5d511c 100644 --- a/source/strings.hpp +++ b/source/strings.hpp @@ -72,15 +72,6 @@ #define S_BLUR_SUBTYPE_ROTATIONAL "Blur.Subtype.Rotational" #define S_BLUR_SUBTYPE_ZOOM "Blur.Subtype.Zoom" -#define S_MIPGENERATOR "MipGenerator" -#define S_MIPGENERATOR_POINT "MipGenerator.Point" -#define S_MIPGENERATOR_LINEAR "MipGenerator.Linear" -#define S_MIPGENERATOR_SHARPEN "MipGenerator.Sharpen" -#define S_MIPGENERATOR_SMOOTHEN "MipGenerator.Smoothen" -#define S_MIPGENERATOR_BICUBIC "MipGenerator.Bicubic" -#define S_MIPGENERATOR_LANCZOS "MipGenerator.Lanczos" -#define S_MIPGENERATOR_INTENSITY "MipGenerator.Intensity" - #define S_CHANNEL_RED "Channel.Red" #define S_CHANNEL_GREEN "Channel.Green" #define S_CHANNEL_BLUE "Channel.Blue"