/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include "gpu/gpu_renderer.h" #include "log/log.h" using namespace GS::GPU; //------------------------------------------------------------------------------ Renderer::RenderTechnique Renderer::GetDefaultRenderTechnique() const { String technique = registry.GetString("Technique", "Forward"); if (technique == "Deferred") return TechniqueDeferred; return TechniqueForward; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Renderer::FreeRenderTechnique() { for (uint n = 0; n < 4; ++n) t_gbuffer[n] = NULL; t_depth = NULL; t_color_aa = NULL; t_depth_aa = NULL; } void Renderer::SetRenderTechnique(RenderTechnique p) { if (p == TechniqueDefault) p = GetDefaultRenderTechnique(); // Filter out illegal configurations. if (!gpu_config.use_rtt && (p == TechniqueDeferred)) p = TechniqueForward; FreeRenderTechnique(); render_technique = p; // Select AA method. Render::Texture::AA aa = Render::Texture::NoAA; if ((p == TechniqueForward) && gpu_config.can_resolve_msaa) if (registry.GetBool("Antialiasing:Enable", false)) { float sample = registry.GetReal("Antialiasing:Sample", 4.0); if (sample > 8.f) aa = Render::Texture::MSAA16x; else if (sample > 4.f) aa = Render::Texture::MSAA8x; else if (sample > 2.f) aa = Render::Texture::MSAA4x; else if (sample > 1.f) aa = Render::Texture::MSAA2x; } gpu_config.enable_aa = asbool(aa != Render::Texture::NoAA); // Setup technique objects. __LOG__ << "SetRenderTechnique(): " << dimensions.x << "x" << dimensions.y << "\n"; bool use_float = registry.GetBool("Texture:Float:Enable", false); Render::Texture::Format t_format = use_float ? Render::Texture::FormatRGBAF : Render::Texture::FormatRGBA8; for (uint n = 0; n < 2; ++n) { t_compose[n] = NewTexture(String("t_compose") << n); t_compose[n]->Create(NULL, dimensions.x, dimensions.y, t_format, Render::Texture::NoAA, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource)); t_compose[n]->SetFiltering(Render::TextureParm::FilterTrilinear); t_compose[n]->SetWrapping(Render::TextureParm::WrapClamp, Render::TextureParm::WrapClamp); } switch (p) { case TechniqueForward: { if (gpu_config.can_resolve_msaa) { t_color_aa = NewTexture("t_color_aa"); t_color_aa->Create(NULL, dimensions.x, dimensions.y, t_format, aa, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource)); t_depth_aa = NewTexture("t_depth_aa"); t_depth_aa->Create(NULL, dimensions.x, dimensions.y, Render::Texture::FormatDepth, aa, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource)); buffer_fbo->SetColorTexture(t_color_aa); buffer_fbo->SetDepthTexture(t_depth_aa); } if (gpu_config.use_rtt) { t_depth = NewTexture("t_depth"); t_depth->Create(NULL, dimensions.x, dimensions.y, Render::Texture::FormatDepth, Render::Texture::NoAA, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource)); resolve_fbo->SetColorTexture(t_compose[0]); resolve_fbo->SetDepthTexture(t_depth); } } break; case TechniqueDeferred: { for (uint n = 0; n < 4; ++n) { t_gbuffer[n] = NewTexture(String("t_gbuffer") << n); t_gbuffer[n]->Create(NULL, dimensions.x, dimensions.y, Render::Texture::FormatRGBAF, Render::Texture::NoAA, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource)); t_gbuffer[n]->SetFiltering(Render::TextureParm::FilterNearest); } t_depth = NewTexture("t_depth"); t_depth->Create(NULL, dimensions.x, dimensions.y, Render::Texture::FormatDepth, Render::Texture::NoAA, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource)); Render::Texture *rt[4] = { t_gbuffer[0], t_gbuffer[1], t_gbuffer[2], t_gbuffer[3] }; buffer_fbo->SetColorTexture(rt, 4); buffer_fbo->SetDepthTexture(t_depth); resolve_fbo->SetColorTexture(t_compose[0]); resolve_fbo->SetDepthTexture(t_depth); } break; } SetPostProcess(); } void Renderer::SetPostProcess(uint k) { if (!gpu_config.use_rtt) return; if (k == 0) k = registry.GetInteger("PostProcess:FX:Scale", 4); for (uint n = 0; n < 3; ++n) { t_fx[n] = NewTexture(String("t_fx") << n); t_fx[n]->Create(NULL, dimensions.x / k, dimensions.y / k, Render::Texture::FormatRGBAF, Render::Texture::NoAA, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource)); t_fx[n]->SetFiltering(Render::TextureParm::FilterBilinear); t_fx[n]->SetWrapping(Render::TextureParm::WrapClamp, Render::TextureParm::WrapClamp); } t_fx_depth = NewTexture("t_fx_depth"); t_fx_depth->Create(NULL, dimensions.x / k, dimensions.y / k, Render::Texture::FormatDepth, Render::Texture::NoAA, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource)); t_fx_depth->SetFiltering(Render::TextureParm::FilterNearest); t_fx_depth->SetWrapping(Render::TextureParm::WrapClamp, Render::TextureParm::WrapClamp); fx_scale = k; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Renderer::SetupCoreResources(bool support_3d) { // Core shaders & objects. if (!LoadCoreShaders(support_3d)) return false; resolve_fbo = NewFBO(); resolve_fbo->Create(); if (support_3d) { buffer_fbo = NewFBO(); buffer_fbo->Create(); fx_fbo = NewFBO(); fx_fbo->Create(); // Shadow mapping objects. if (gpu_config.enable_shadow) { shadow_map_fbo = NewFBO(); shadow_map_fbo->Create(); CreateShadowMaps(); } // Default technique. SetRenderTechnique(); if (!CreateTerrainPatch()) return false; t_noise = core_resource_factory->LoadTexture("@core/noise.tga"); } { static const ushort idx[] = { 2, 1, 0, 3, 2, 0 }; static const float vtx[] = { -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1 }; skybox_idx_vbo = NewVBO(); skybox_idx_vbo->Create(idx, sizeof(ushort) * 6, VBO::Index); skybox_vtx_vbo = NewVBO(); skybox_vtx_vbo->Create(vtx, sizeof(float) * 3 * 4, VBO::Vertex); helper_idx_vbo = NewVBO(); helper_idx_vbo->Create(idx, sizeof(ushort) * 6, VBO::Index); helper_vtx_vbo = NewVBO(); helper_vtx_vbo->Create(sizeof(float) * 256, VBO::Vertex, VBO::Dynamic); // 1Kb WARNING watch out for possible overflow when updating this buffer! ushort box_idx[] = { 0, 1, 2, 0, 2, 3, 1, 5, 6, 1, 6, 2, 5, 4, 7, 5, 7, 6, 4, 0, 3, 4, 3, 7, 4, 5, 1, 4, 1, 0, 3, 2, 6, 3, 6, 7 }; box_idx_vbo = NewVBO(); box_idx_vbo->Create(box_idx, sizeof(ushort) * 3 * 4 * 3, VBO::Index); direct_idx_vbo = NewVBO(); direct_vtx_vbo = NewVBO(); } return true; } void Renderer::SetDefaultStates() { // Set defaults states. EnableDepthTest(true); SetDepthFunc(DepthLess); EnableCulling(true); SetCullFunc(CullFront); EnableBlending(false); SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Renderer::DiscoverGPUConfiguration() { Variant v; if (QueryCaps(CanBlitRBO, v)) gpu_config.can_resolve_msaa = v.b_value; if (QueryCaps(MaxAnisotropy, v)) gpu_config.max_anisotropy = v.i_value; if (QueryCaps(TextureTopLeftOrigin, v)) gpu_config.tex_origin_is_top_left = v.b_value; #if __PLATFORM_ANDROID_NDK__ || __PLATFORM_IOS__ || __PLATFORM_EMSCRIPTEN__ // Drop all advanced features... for now. gpu_config.enable_shadow = false; gpu_config.can_resolve_msaa = false; gpu_config.use_rtt = false; gpu_config.npot = RendererConfig::NPOT_Limited; #endif if (!gpu_config.can_resolve_msaa) __LOG_W__ << "Insufficient RBO support, falling back to RTT: No hardware MSAA.\n"; if (!gpu_config.enable_shadow) __LOG_W__ << "Shadow-mapping support disabled.\n"; if (gpu_config.npot == RendererConfig::NPOT_Limited) __LOG_V__ << "NPOT limited support.\n"; if (gpu_config.npot == RendererConfig::NPOT_Full) __LOG_V__ << "NPOT full support.\n"; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Renderer::ResizeVideo(uint w, uint h) { __LOG_V__ << "GPU Resize video to " << w << "x" << h << ".\n"; dimensions.Set(w, h); SetRenderTechnique(render_technique); return true; } bool Renderer::Open(uint w, uint h, char bpp, GS::Render::VideoMode mode, const void *sys_handle) { if (!OpenPlatformVideo(w, h, bpp, mode, sys_handle)) return false; if (!InitializePlatform()) return false; __LOG__ << "\n"; __LOG_H__ << "GPU-based (" << GetName() << ") on adapter " << stats.adapter << " (vendor: " << stats.vendor << ").\n"; __LOG__ << "\n"; DiscoverGPUConfiguration(); dimensions.Set(w, h); SetViewport(fRect(0, 0, (float)w, (float)h)); return true; } void Renderer::Free() { __LOG_FUNC__ terrain_patch_cache.Free(); FreeRenderTechnique(); helper_idx_vbo = NULL; helper_vtx_vbo = NULL; skybox_idx_vbo = NULL; skybox_vtx_vbo = NULL; direct_idx_vbo = NULL; direct_vtx_vbo = NULL; box_idx_vbo = NULL; buffer_fbo = NULL; resolve_fbo = NULL; fx_fbo = NULL; for (uint n = 0; n < 2; ++n) t_compose[n] = NULL; for (uint n = 0; n < 3; ++n) t_fx[n] = NULL; t_fx_depth = NULL; fx_scale = 0; t_noise = NULL; FreeShadowMaps(); shadow_map_fbo = NULL; UnloadCoreShaders(); } void Renderer::Close() { Free(); ClosePlatformVideo(); } //------------------------------------------------------------------------------