/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include #include "gpu/gpu_renderer.h" #include "gpu/gpu_geometry.h" #include "gpu/gpu_shader_object.h" #include "gpu/gpu_draw_context.h" #include "core/geometry.h" #include "rand/rand.h" #include "log/log.h" using namespace GS; using namespace GPU; //------------------------------------------------------------------------------ static bool IsPostProcessExcluded(NML::Tag *exclusion_key, const char *tag) { NML::Tag *__t = exclusion_key ? exclusion_key->GetTypedTag(tag, Variant::VariantBool) : NULL; return __t && __t->GetBool(); } //------------------------------------------------------------------------------ //--------------------------------------------------------------- #define _SwapTarget { tmp = t[0]; t[0] = t[1]; t[1] = tmp; } //--------------------------------------------------------------- //------------------------------------------------------------------------------ void Renderer::GetPostProcessNormalDepth(const Stack display_lists[2]) { if (normal_depth_updated) return; ScopedPerfEvent event(this, __FUNCTION__, Color::Purple); fRect old_viewport = GetViewport(); // Generate normal/depth buffer. SetViewport(fRect(0, 0, (float)t_fx[2]->GetWidth(), (float)t_fx[2]->GetHeight())); fx_fbo->SetColorTexture(t_fx[2]); if (render_technique == TechniqueDeferred) { SetCurrentFBO(fx_fbo); RenderFullscreenQuad(*single_texture_program, old_viewport.GetWidth() / (float)dimensions.x, old_viewport.GetHeight() / (float)dimensions.y, t_gbuffer[0]); } else { fx_fbo->SetDepthTexture(t_fx_depth); SetCurrentFBO(fx_fbo); Clear(0, 0, 0); DrawContext dc(DrawContext::Opaque, DrawContext::Base, MaterialShader::PP_NormalDepth); DrawList(display_lists[0], dc); fx_fbo->SetDepthTexture(NULL); } SetViewport(old_viewport); normal_depth_updated = true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Renderer::ApplyDirectionalBlur(Render::Texture *t[2], const Vector2 &d, float attn) { if (!fx_blur_program) return; Render::Texture *tmp; ShaderInput *u_pass = fx_blur_program->GetInput("u_pass"), *u_blur_d = fx_blur_program->GetInput("u_blur_d"), *u_attenuation = fx_blur_program->GetInput("u_attenuation"); SetShaderProgram(fx_blur_program); u_blur_d->SetValue(d.x, d.y); u_attenuation->SetValue(attn); fRect nviewport(viewport.sx / dimensions.x, viewport.sy / dimensions.y, viewport.ex / dimensions.x, viewport.ey / dimensions.y); for (int n = 0; n < 4; ++n) { fx_fbo->SetColorTexture(t[1]); SetCurrentFBO(fx_fbo); u_pass->SetValue(float(n)); RenderFullscreenQuad(*fx_blur_program, nviewport, fRect(0, 0, 1, 1), t[0]); _SwapTarget } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Renderer::ApplyBloomFilter(Render::Texture *t_in, float strength, float threshold, float exponent, float radius, float strength_white_screen) { if (!strength || !single_texture_cutoff_program || !tone_mapping_program) return; ScopedPerfEvent event(this, __FUNCTION__, Color::Purple); Render::Texture *t[] = { t_fx[0], t_fx[1] }; ShaderInput *u_cutoff = single_texture_cutoff_program->GetInput("u_cutoff"), *u_strength = tone_mapping_program->GetInput("u_strength"), *u_strength_white_screen = tone_mapping_program->GetInput("u_strength_white_screen"); if (!u_cutoff || !u_strength || !u_strength_white_screen) return; fRect old_viewport = GetViewport(); fRect nviewport(viewport.sx / dimensions.x, viewport.sy / dimensions.y, viewport.ex / dimensions.x, viewport.ey / dimensions.y); fRect fx_viewport(old_viewport.sx / fx_scale, old_viewport.sy / fx_scale, old_viewport.ex / fx_scale, old_viewport.ey / fx_scale); //-------------------------------------------------------------------------- #define CutoffFrameToBloomFX \ { \ SetViewport(fx_viewport); \ \ fx_fbo->SetColorTexture(t[0]); \ SetCurrentFBO(fx_fbo);\ SetShaderProgram(single_texture_cutoff_program); \ u_cutoff->SetValue(threshold); \ RenderFullscreenQuad(*single_texture_cutoff_program, nviewport, fRect(0, 0, 1, 1), t_in); \ } #define OutputBloomFXToFrame \ { \ SetViewport(old_viewport);\ \ EnableBlending(true); \ SetBlendFunc(BlendOne, BlendOne); \ \ fx_fbo->SetColorTexture(t_in); \ SetCurrentFBO(fx_fbo);\ SetShaderProgram(tone_mapping_program); \ u_strength->SetValue(strength * 1.25f); \ u_strength_white_screen->SetValue(strength_white_screen); \ RenderFullscreenQuad(*tone_mapping_program, nviewport, fRect(0, 0, 1, 1), t[0]); \ \ EnableBlending(false); \ } //-------------------------------------------------------------------------- if (view_registry->GetBool("PostProcess:Bloom:Streak:Enabled", false)) { float angle = view_registry->GetReal("PostProcess:Bloom:Streak:Angle"), attn = view_registry->GetReal("PostProcess:Bloom:Streak:Attenuation", 0.975f); for (int n = 0; n < 2; ++n) { Vector2 d = Vector2(cos(angle), sin(angle)) * radius / 12.f; CutoffFrameToBloomFX ApplyDirectionalBlur(t, d, attn); OutputBloomFXToFrame if (!view_registry->GetBool("PostProcess:Bloom:Streak:CrossShaped", false)) break; angle += Units::Deg(90.f); } } else { CutoffFrameToBloomFX ApplyDirectionalBlur(t, Vector2(radius / 64.f, 0)); ApplyDirectionalBlur(t, Vector2(0, radius / 64.f)); OutputBloomFXToFrame } SetViewport(old_viewport); SetClippingRect(&old_viewport); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Renderer::ApplyNoiseFilter(Render::Texture *t_in, Render::Texture *t_out, float strength, float mono, float bias) { if (!strength || !noise_program) return false; ScopedPerfEvent event(this, __FUNCTION__, Color::Purple); ShaderInput *u_strength = noise_program->GetInput("u_strength"), *u_mono = noise_program->GetInput("u_mono"), *u_bias = noise_program->GetInput("u_bias"), *u_random = noise_program->GetInput("u_random"); if (!u_strength || !u_mono || !u_bias || !u_random) return false; fx_fbo->SetColorTexture(t_out); SetCurrentFBO(fx_fbo); float random[2] = { Random::FRand(), Random::FRand() }; SetShaderProgram(noise_program); u_strength->SetValue(strength); u_mono->SetValue(mono); u_bias->SetValue(bias); u_random->SetValue(random[0], random[1]); RenderFullscreenQuad(*noise_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in); return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Renderer::ApplySSAAFilter(Render::Texture *t_in, Render::Texture *t_out) { if (!ssaa_program) return false; ScopedPerfEvent event(this, __FUNCTION__, Color::Purple); fx_fbo->SetColorTexture(t_out); SetCurrentFBO(fx_fbo); SetShaderProgram(ssaa_program); RenderFullscreenQuad(*ssaa_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in); return true; } //------------------------------------------------------------------------------ bool Renderer::ApplyResolveMSAADepth(Render::Texture *t_depth_msaa, Render::Texture *t_out) { if (!t_depth_msaa || !t_out || !resolve_msaa_depth_program) return false; ScopedPerfEvent event(this, __FUNCTION__, Color::Purple); fx_fbo->SetColorTexture(t_out); SetCurrentFBO(fx_fbo); ShaderInput *msaa_depth = resolve_msaa_depth_program->GetInput("u_depthMSAA"); SetShaderProgram(resolve_msaa_depth_program); msaa_depth->SetValue(t_depth_msaa); RenderFullscreenQuad(*resolve_msaa_depth_program,viewport.GetWidth() / (float)dimensions.x,viewport.GetHeight() / (float)dimensions.y); return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Renderer::ApplySSAOFilter(Render::Texture *t_in, Render::Texture *t_out, const Stack display_lists[2], float s, float r, float d, float blur_r) { if (!s || !ssao_program) return false; ScopedPerfEvent event(this, __FUNCTION__, Color::Purple); ShaderInput *u_strength = ssao_program->GetInput("u_strength"), *u_radius = ssao_program->GetInput("u_radius"), *u_distance_scale = ssao_program->GetInput("u_distance_scale"), *u_iproj2d = ssao_program->GetInput("u_iproj2d"); if (!u_strength || !u_radius || !u_distance_scale) return false; GetPostProcessNormalDepth(display_lists); #if 0 // Normal/depth debug output. fx_fbo->SetColorTexture(t_in); SetCurrentFBO(fx_fbo); RenderFullscreenQuad(single_texture_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_fx[2]); return false; #endif fRect old_viewport = GetViewport(); SetViewport(fRect(0, 0, (float)t_fx[2]->GetWidth(), (float)t_fx[2]->GetHeight())); // Render raw SSAO to FX texture. SetShaderProgram(ssao_program); u_strength->SetValue(s); u_radius->SetValue(r * (4 / fx_scale)); u_distance_scale->SetValue(d); float iproj[] = { 1.f / m_projection.m[0][0], 1.f / m_projection.m[1][1] }; if (!gpu_config.tex_origin_is_top_left) iproj[1] = -iproj[1]; u_iproj2d->SetValue(iproj[0], iproj[1]); EnableAlphaTest(false); EnableBlending(false); int out_index = 0; fx_fbo->SetColorTexture(t_fx[out_index]); SetCurrentFBO(fx_fbo); Clear(0, 0, 0, 0, 1, ClearColor); // FIXME this is USELESS, but something is killing fragments with 0 alpha in ssao_program and disabling alpha testing does not solves the issue. RenderFullscreenQuad(*ssao_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_fx[2]); if (ssao_blur_program) { // SSAO blur. if (blur_r > 0.f) { ShaderInput *u_blur_radius = ssao_blur_program->GetInput("u_blur_radius"), *u_normal_depth = ssao_blur_program->GetInput("u_normal_depth"); // the normal depth texture is not always used, depending on the quality settings. if (u_blur_radius && u_normal_depth) { fx_fbo->SetColorTexture(t_fx[1 - out_index]); SetCurrentFBO(fx_fbo); Clear(0, 0, 0, 0, 1, ClearColor); // FIXME this is USELESS, but something is killing fragments with 0 alpha in ssao_program and disabling alpha testing does not solves the issue. SetShaderProgram(ssao_blur_program); u_blur_radius->SetValue(blur_r); u_normal_depth->SetValue(t_fx[2].c_ptr()); RenderFullscreenQuad(*ssao_blur_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_fx[out_index]); out_index = 1 - out_index; } } } // Composite back over input. SetViewport(old_viewport); fx_fbo->SetColorTexture(t_in); SetCurrentFBO(fx_fbo); EnableBlending(true); SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha); RenderFullscreenQuad(*single_texture_program, 1, 1, t_fx[out_index]); EnableBlending(false); return false; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Renderer::ApplySharpenFilter(Render::Texture *t_in, Render::Texture *t_out, float strength) { if (!strength || !sharpen_program) return false; ScopedPerfEvent event(this, __FUNCTION__, Color::Purple); ShaderInput *u_strength = sharpen_program->GetInput("u_strength"); if (!u_strength) return false; fx_fbo->SetColorTexture(t_out); SetCurrentFBO(fx_fbo); SetShaderProgram(sharpen_program); u_strength->SetValue(strength); RenderFullscreenQuad(*sharpen_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in); return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Renderer::ApplyHSL(Render::Texture *t_in, Render::Texture *t_out, float H, float S, float L) { if (((H == 1.f) && (S == 1.f) && (L == 1.f)) || !hsl_program) return false; ScopedPerfEvent event(this, __FUNCTION__, Color::Purple); ShaderInput *u_H = hsl_program->GetInput("u_H"), *u_S = hsl_program->GetInput("u_S"), *u_L = hsl_program->GetInput("u_L"); if (!u_H || !u_S || !u_L) return false; fx_fbo->SetColorTexture(t_out); SetCurrentFBO(fx_fbo); SetShaderProgram(hsl_program); u_H->SetValue(H); u_S->SetValue(S); u_L->SetValue(L); RenderFullscreenQuad(*hsl_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in); return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Renderer::ApplyChromaticDispersion(Render::Texture *t_in, Render::Texture *t_out, float width) { if ((width == 0.f) || !chromatic_dispersion_program) return false; ScopedPerfEvent event(this, __FUNCTION__, Color::Purple); ShaderInput *u_width = chromatic_dispersion_program->GetInput("u_width"); if (!u_width) return false; fx_fbo->SetColorTexture(t_out); SetCurrentFBO(fx_fbo); SetShaderProgram(chromatic_dispersion_program); u_width->SetValue(width); RenderFullscreenQuad(*chromatic_dispersion_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in); return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Renderer::ApplyRadialBlur(Render::Texture *t_in, Render::Texture *t_out, float strength, float center_x, float center_y) { if (!strength || !radial_blur_program) return false; ScopedPerfEvent event(this, __FUNCTION__, Color::Purple); ShaderInput *u_strength = radial_blur_program->GetInput("u_strength"), *u_center = radial_blur_program->GetInput("u_center"); if (!u_strength || !u_center) return false; fx_fbo->SetColorTexture(t_out); SetCurrentFBO(fx_fbo); SetShaderProgram(radial_blur_program); u_strength->SetValue(strength); float center[] = { center_x * viewport.GetWidth() / (float)dimensions.x, center_y * viewport.GetHeight() / (float)dimensions.y }; u_center->SetValue(center[0], center[1]); RenderFullscreenQuad(*radial_blur_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in); return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Renderer::ApplyMotionBlur(Render::Texture *t_in, Render::Texture *t_out, const Stack display_lists[2], float strength, int quality) { if (!strength || !quality || !motion_blur_program) return false; ScopedPerfEvent event(this, __FUNCTION__, Color::Purple); float k_u = viewport.GetWidth() / dimensions.x, k_v = viewport.GetHeight() / dimensions.y; // Generate a velocity buffer from the opaque surface list. fRect old_viewport = GetViewport(); { ScopedPerfEvent event(this, "Render Velocity Buffer", Color::Purple); SetViewport(fRect(0, 0, (float)t_fx[0]->GetWidth(), (float)t_fx[0]->GetHeight())); fx_fbo->SetColorTexture(t_fx[0]); fx_fbo->SetDepthTexture(t_fx_depth); SetCurrentFBO(fx_fbo); Clear(0, 0, 0); DrawContext dc(DrawContext::Opaque, DrawContext::Base, MaterialShader::PP_Velocity); DrawList(display_lists[0], dc); } // Vector field blur. #if 0 float k_blur = 0.5; Render::Texture *t_pp_0[] = { t_fx[0], t_fx[1] }; ApplyDirectionalBlur(t_pp_0, nVector2(k_blur, 0), 0.975f); Render::Texture *t_pp_1[] = { t_fx[1], t_fx[0] }; ApplyDirectionalBlur(t_pp_1, nVector2(0, k_blur), 0.975f); #endif // Apply velocity field. fx_fbo->SetDepthTexture(NULL); // Quality: 0 - No PP, 1 - Lores blur, 2 - Hires blur. if (quality == 1) { fx_fbo->SetColorTexture(t_fx[1]); SetCurrentFBO(fx_fbo); SetShaderProgram(single_texture_program); RenderFullscreenQuad(*single_texture_program, 1, 1, t_in); } else { fx_fbo->SetColorTexture(t_out); SetCurrentFBO(fx_fbo); SetViewport(old_viewport); } ShaderInput *u_strength = motion_blur_program->GetInput("u_strength"), *u_pow = motion_blur_program->GetInput("u_pow"), *u_max = motion_blur_program->GetInput("u_max"), *u_source = motion_blur_program->GetInput("u_source"), *u_velocity = motion_blur_program->GetInput("u_velocity"); SetShaderProgram(motion_blur_program); const float k = 1.f; // stats.bench_fps.GetFps() / 60.f; // Normalize to 60fps. u_strength->SetValue(strength * 32.f * k); u_pow->SetValue(2.f); u_max->SetValue(1.f / 128.f); // Works in normalized space, does not need to adjust for variable resolution. motion_blur_program->Set(*u_velocity->location, *t_fx[0], u_velocity->index); if (quality == 1) // low-res blur path { fx_fbo->SetColorTexture(t_fx[2]); SetCurrentFBO(fx_fbo); u_source->SetValue(t_fx[1].c_ptr()); RenderFullscreenQuad(*motion_blur_program, 1, 1); fx_fbo->SetColorTexture(t_in); SetCurrentFBO(fx_fbo); SetViewport(old_viewport); EnableBlending(true); SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha); SetShaderProgram(single_texture_program); RenderFullscreenQuad(*single_texture_program, k_u, k_v, t_fx[2]); EnableBlending(false); return false; } u_source->SetValue(t_in); RenderFullscreenQuad(*motion_blur_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y); return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ Render::Texture *Renderer::ApplyPostProcessChain(const Stack display_lists[2]) { ScopedBenchmark bench(stats.bench_post_process); ScopedPerfEvent event(this, "Apply Post-Processing Chain", Color::Green); if (!gpu_config.use_rtt) return t_compose[0]; normal_depth_updated = false; NML::Tag *post_process_registry = view_registry ? view_registry->GetTag("PostProcess") : NULL; if (!post_process_registry) return t_compose[0]; NML::Tag *exclusion_key = registry.GetTag("PostProcess:Exclusion"); fRect old_clipping = GetClippingRect(); SetClippingRect(NULL); uint i_compose = 0; NML::Tag *tag; // SSAO. #if !(__PLATFORM_ANDROID_NDK__ || __PLATFORM_IOS__) if (((tag = post_process_registry->GetTag("SSAO")) != NULL) && !IsPostProcessExcluded(exclusion_key, "SSAO")) if (ApplySSAOFilter ( t_compose[i_compose], t_compose[1 - i_compose], display_lists, view_registry->GetReal("PostProcess:SSAO:Strength", 1), view_registry->GetReal("PostProcess:SSAO:Radius", 2), view_registry->GetReal("PostProcess:SSAO:DistanceScale", 0.5), view_registry->GetReal("PostProcess:SSAO:BlurRadius", 8) )) i_compose = 1 - i_compose; #endif // Sharpen. if (((tag = post_process_registry->GetTag("Sharpen")) != NULL) && !IsPostProcessExcluded(exclusion_key, "Sharpen")) if (ApplySharpenFilter ( t_compose[i_compose], t_compose[1 - i_compose], view_registry->GetReal("PostProcess:Sharpen:Strength", 0.5f) )) i_compose = 1 - i_compose; // SSAA. if (render_technique == TechniqueDeferred) if (registry.GetBool("Antialiasing:Enable", false)) if (ApplySSAAFilter ( t_compose[i_compose], t_compose[1 - i_compose] )) i_compose = 1 - i_compose; #if !(__PLATFORM_ANDROID_NDK__ || __PLATFORM_IOS__) // Motion blur. if (((tag = post_process_registry->GetTag("MotionBlur")) != NULL) && !IsPostProcessExcluded(exclusion_key, "MotionBlur")) if (ApplyMotionBlur ( t_compose[i_compose], t_compose[1 - i_compose], display_lists, view_registry->GetReal("PostProcess:MotionBlur:Strength", 0.5), registry.GetInteger("PostProcess:MotionBlur:Quality", 2) )) i_compose = 1 - i_compose; #endif // Radial blur. if (((tag = post_process_registry->GetTag("RadialBlur")) != NULL) && !IsPostProcessExcluded(exclusion_key, "RadialBlur")) if (ApplyRadialBlur ( t_compose[i_compose], t_compose[1 - i_compose], view_registry->GetReal("PostProcess:RadialBlur:Strength", 0.5), view_registry->GetReal("PostProcess:RadialBlur:CenterX", 0.5), view_registry->GetReal("PostProcess:RadialBlur:CenterY", 0.5) )) i_compose = 1 - i_compose; // Bloom. if (((tag = post_process_registry->GetTag("Bloom")) != NULL) && !IsPostProcessExcluded(exclusion_key, "Bloom")) ApplyBloomFilter ( t_compose[i_compose], view_registry->GetReal("PostProcess:Bloom:Strength", 1), view_registry->GetReal("PostProcess:Bloom:Threshold", 0.95f), view_registry->GetReal("PostProcess:Bloom:Exponent", 4), view_registry->GetReal("PostProcess:Bloom:Radius", 12), view_registry->GetReal("PostProcess:Sharpen:Strength", 0.5f) ); // Noise. #if !(__PLATFORM_ANDROID_NDK__ || __PLATFORM_IOS__) // TODO mobile version of this. if (((tag = post_process_registry->GetTag("Noise")) != NULL) && !IsPostProcessExcluded(exclusion_key, "Noise")) if (ApplyNoiseFilter ( t_compose[i_compose], t_compose[1 - i_compose], view_registry->GetReal("PostProcess:Noise:Strength", 0.25f), view_registry->GetReal("PostProcess:Noise:Monochromatic", 0.f), view_registry->GetReal("PostProcess:Noise:LumaBias", 0.5f) ) ) i_compose = 1 - i_compose; #endif // HSL. if (((tag = post_process_registry->GetTag("HueSaturation")) != NULL) && !IsPostProcessExcluded(exclusion_key, "HueSaturation")) if (ApplyHSL ( t_compose[i_compose], t_compose[1 - i_compose], view_registry->GetReal("PostProcess:HueSaturation:H", 1), view_registry->GetReal("PostProcess:HueSaturation:S", 1), view_registry->GetReal("PostProcess:HueSaturation:L", 1) ) ) i_compose = 1 - i_compose; // Chromatic dispersion. if (((tag = post_process_registry->GetTag("ChromDisp")) != NULL) && !IsPostProcessExcluded(exclusion_key, "ChromDisp")) if (ApplyChromaticDispersion ( t_compose[i_compose], t_compose[1 - i_compose], view_registry->GetReal("PostProcess:ChromDisp:Width", 1) ) ) i_compose = 1 - i_compose; SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha); SetClippingRect(&old_clipping); SetIndexSource(NULL); SetVertexSource(NULL, 0); return t_compose[i_compose]; } //------------------------------------------------------------------------------