/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include #include "gpu/gpu_renderer.h" #include "gpu/gpu_geometry.h" #include "core/renderer_environment_interface.h" #include "core/terrain.h" #include "core/geometry.h" #include "sort/sort.h" #include "container/container_sort.h" #include "platform_config.h" #include "log/log.h" using namespace GS; using namespace GPU; //------------------------------------------------------------------------------ void Renderer::DrawList(const Stack &dl_list, const DrawContext &dc) { ResetDisplayListCache(); DecayTerrainCache(); m_previous_iview = view_item->GetPreviousMatrix().InversedFast(); for (uint n = 0; n < dl_list.GetCount(); ++n) { RenderPrimitive *dl = dl_list[n]; if (!dl->item) continue; switch (dl->type) { case RenderPrimitive::TypeDisplayList: if (dl->dlst) { m_previous_world = dl->item->GetPreviousMatrix(); SetWorldMatrix(dl->item->GetMatrix(), &dl->item->GetInverseMatrix()); DrawDisplayListCached(*dl->dlst, *dl->item, dc); } break; case RenderPrimitive::TypeTerrainPatch: if (dl->patch) RenderTerrainPatch(*dl->patch, *dl->item, dc); break; case RenderPrimitive::TypeEmitter: break; } } ResetDisplayListCache(); stats.queue_pass++; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ static inline uint GetRenderPrimitiveSortKey(RenderPrimitive *a) { // Watch nRenderMaterial and nGPUDisplayList size in bytes so that this key stays optimal. return ((((uint)a->dlst->material.c_ptr() >> 8) & 0xffff) << 16) + (((uint)a->dlst >> 7) & 0xffff); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Renderer::SortDisplayList(AutoStack &d_list) const { const uint count = d_list.GetCount(); if (count == 0) return; try { // Byte sort list. typedef Sort SortPrimitive; Array sort_a(count), sort_b(count); for (uint n = 0; n < count; ++n) { sort_a[n].o = d_list[n]; sort_a[n].v = GetRenderPrimitiveSortKey(d_list[n]); } Array *out = SortPrimitive::ByteSort(count, &sort_a, &sort_b); // Empty the source stack, prevent primitives cleanup... d_list.DropContentOwnership(); // ...as we now insert them back in sorted order. for (uint n = 0; n < count; ++n) d_list.Push((*out)[n].o); } catch(char *e) { __LOG__ << "Failed to SortDisplayList.\n"; } } static int GetMaterialDrawPassIndex(Render::Material *m, float opacity = 1.f) { if (m->renderword & Core::Material::Render_UseFramebuffer) return 2; if ((m->blendop != Core::Material::Blend_None) || (opacity < 1.0)) return 1; return 0; } void Renderer::BuildDisplayLists(const AutoStack &p_list, AutoStack *d_list) const { for (uint n = 0; n < p_list.GetCount(); ++n) { Render::Primitive *p = p_list[n]; switch (p->type) { case Render::Primitive::Type_Geometry: if (Geometry *geo = (Geometry *)p->geometry.c_ptr()) for (uint n = 0; n < geo->display_list.GetCount(); ++n) { DisplayList *dlist = geo->display_list[n]; if (dlist->material.IsNull()) continue; int draw_pass_index = GetMaterialDrawPassIndex(dlist->material, p->item->opacity); d_list[draw_pass_index].Push(new RenderPrimitive(p->item, dlist)); } break; case Render::Primitive::Type_TerrainPatch: if (p->patch->terrain->render_data->material.IsNull()) continue; d_list[GetMaterialDrawPassIndex(p->patch->terrain->render_data->material, p->patch->terrain->opacity)].Push(new RenderPrimitive(p->item, p->patch)); break; case Render::Primitive::Type_Emitter: break; } } for (int n = 0; n < 3; ++n) SortDisplayList(d_list[n]); } void Renderer::RenderList() { ScopedBenchmark bench(stats.bench_render); PerfSetMarker("RenderList", Color::Green); // Build primitive list. stats.renderable_processed += BuildRenderablePrimitiveList(*view_item, *view_item, frustum_rlist.primitive_list); stats.renderable_drawn += frustum_rlist.primitive_list.GetCount(); // Build display lists. BuildDisplayLists(frustum_rlist.primitive_list, frustum_rlist.display_lists); // Render opaque display lists. switch (render_technique) { case TechniqueDeferred: RenderListDeferred(frustum_rlist.display_lists[0]); break; case TechniqueForward: RenderListForward(frustum_rlist.display_lists[0], DrawContext::Opaque); if (environment_interface) // draw skybox { Render::Shader *user_skybox_shader = environment_interface->GetSkyboxShader(); Render::sTexture skybox_layers[2]; if (environment_interface->GetSkyboxLayers(skybox_layers) || user_skybox_shader) DrawSkybox(skybox_layers, user_skybox_shader); } break; } // Render transparent display lists. RenderListForward(frustum_rlist.display_lists[1], DrawContext::Alpha); // Render frame buffer dependent display lists. if (gpu_config.use_rtt && ((frustum_rlist.display_lists[2].GetCount() > 0) || registry.GetBool("FrameBufferAsTexture;", false))) { GrabDisplay(t_fx[0]); RenderListForward(frustum_rlist.display_lists[2], DrawContext::Opaque); } // Render direct user primitives hook. if (environment_interface) environment_interface->OnRenderUser(this); // switch (render_technique) { case TechniqueDeferred: // Render fog. if (environment_interface) if (environment_interface->IsFogEnabled() && !performance_tools.disable_fog) { EnableBlending(true); RenderFullscreenQuad(*ds_fog_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y); EnableBlending(false); } break; case TechniqueForward: // Resolve MSSA target. if (gpu_config.enable_aa) render_fbo->Blit(resolve_fbo, iRect(0, 0, dimensions.x, dimensions.y), iRect(0, 0, dimensions.x, dimensions.y), true, true); break; } // Post-processes. if (gpu_config.use_rtt) { t_final = ApplyPostProcessChain(frustum_rlist.display_lists); resolve_fbo->SetColorTexture(t_final); } // Drop render list. frustum_rlist.Clear(false); // Subsequent draws should render to the final texture. if (gpu_config.use_rtt) SetCurrentFBO(resolve_fbo); render_fbo = resolve_fbo; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Renderer::BeginDrawList() { PerfSetMarker("BeginDrawList", Color::Green); // Cache per-frame values for coherency. frame_clock = environment_interface ? environment_interface->GetClock() : 0; pcf_radius = registry.GetReal("ShadowMapping:PCF:Radius", 1.75f); if (environment_interface) if (Core::Camera *c = environment_interface->GetCurrentCamera()) { SetCamera(c); ApplyCamera(); } /* MSAA needs a special resolve step so we work on a different FBO which will later be resolved to the resolve FBO. */ render_fbo = gpu_config.enable_aa ? buffer_fbo : resolve_fbo; if (render_technique == TechniqueForward) { if (gpu_config.use_rtt) SetCurrentFBO(render_fbo); // Clear output, if a skybox is set it will be rendered after the opaque pass. if (environment_interface) { Render::Shader *user_skybox_shader = environment_interface->GetSkyboxShader(); Render::sTexture skybox_layers[2]; if (environment_interface->GetSkyboxLayers(skybox_layers) || user_skybox_shader) Clear(0, 0, 0, 1, 1, ClearDepth); else { Color bg = environment_interface->GetClearColor(); Clear(bg.x, bg.y, bg.z); } } } t_final = t_compose[0]; return true; } void Renderer::EndDrawList() { PerfSetMarker("EndDrawList", Color::Green); // when entering this function the current FBO is expected to be render_FBO. if (gpu_config.use_rtt) { // Blit result to user output_fbo. if (output_fbo) render_fbo->Blit(output_fbo, viewport.AsInt(), output_fbo_rect, true, false); // Blit to frame buffer. SetCurrentFBO(NULL); fRect src(viewport.sx / dimensions.x, viewport.sy / dimensions.y, viewport.ex / dimensions.x, viewport.ey / dimensions.y); RenderFullscreenQuad(*single_texture_program, src, fRect(0, 0, 1, 1), t_final); // viewport is already set for destination so use the full rect output // Restore default output. resolve_fbo->SetColorTexture(t_final = t_compose[0]); } SetIndexSource(NULL); SetVertexSource(NULL, 0); } //------------------------------------------------------------------------------